none
Ordem de precedência de parâmetros influenciando em resultados de SELECT RRS feed

  • Pergunta

  • Pessoal estou com um problema para fazer a extração de informações de uma base de dados.

    Como tem muitos anos que não atuo nessa área, estou com muita dificuldade para solucionar a questão e preciso de ajuda.

    SELECT chave_fato, cod_filial, cod_tipo_mv, descricao, num_docto, cod_cli_for, nome_cadastro, CONVERT(CHAR, data_v2, 103) AS 'data emissao', REPLACE(valor_total,'.',',') AS 'Valor total',
    REPLACE(valor_liquido,'.',',') AS 'Valor liquido', desc_divisao, desc_produto_nf, qtde_pri, cod_unidade_pri, qtde_aux, REPLACE(valor_liquido_item,'.',',') AS 'valor liquido item',
    status_ctb, MONTH(data_v2) AS 'Mês', DAY(data_v2) AS 'Dia'
    from vwMovtoEntrada
    --WHERE DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' AND desc_divisao LIKE '%bov%' AND COD_TIPO_MV = '811' OR COD_TIPO_MV = 'T816' OR COD_TIPO_MV = 'T819'
    --WHERE cod_tipo_mv IN ('811','T816','T819') AND data_v2 BETWEEN '2019-05-01' AND '2019-05-31' --AND desc_divisao LIKE '%BOV%'
    --WHERE data_v2 BETWEEN '2019-05-01' AND '2019-05-31' AND desc_divisao LIKE '%bov%' AND cod_tipo_mv IN ('811','T816','T819')
    --WHERE DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' AND COD_TIPO_MV = '811' OR COD_TIPO_MV = 'T816' OR COD_TIPO_MV = 'T819' AND desc_divisao LIKE '%bov%'
    --WHERE DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' AND desc_divisao LIKE '%bov%' AND COD_TIPO_MV = '811' OR COD_TIPO_MV = 'T816' OR COD_TIPO_MV = 'T819'
    --WHERE DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' AND COD_TIPO_MV = '811' OR COD_TIPO_MV = 'T816' OR COD_TIPO_MV = 'T819' AND desc_divisao LIKE '%bov%'
    --WHERE data_v2 BETWEEN '2019-05-01' AND '2019-05-31' AND desc_divisao LIKE '%bov%' AND cod_tipo_mv IN ('811','T816','T819')
    ORDER BY data_v2 ASC

    Cada um dos códigos condicionais me retorna um resultado diferente.

    Qual a ordem correta dos argumentos?



    terça-feira, 27 de agosto de 2019 12:55

Respostas

  • Flávio,

    Bom, a questão de uma possível ordem correta, poderá variar de acordo com a necessidade, mas sabemos que sempre o operador AND exige que todas as condições sejam satisfatórias "verdadeiras" para o resultado ser apresentado, ao contrário do OR que no mínimo uma das condições e regras aplicadas sendo verdadeira algo será retornado.

    O que necessariamente esta query deverá retornar?

    Inicialmente eu aplicaria na cláusula query um filtro trazendo somente os COD_TIPO_MV, fazendo uso do operador IN, que por detrás dos panos faz a mesma coisa que OR, mas acaba se tornando mais legível para o entendimento do código, veja esta alteração:

    SELECT chave_fato, cod_filial, cod_tipo_mv, 
           descricao, num_docto, cod_cli_for, 
           nome_cadastro, CONVERT(CHAR, data_v2, 103) AS 'data emissao', 
           REPLACE(valor_total,'.',',') AS 'Valor total',
           REPLACE(valor_liquido,'.',',') AS 'Valor liquido', 
           desc_divisao, 
           desc_produto_nf, 
           qtde_pri, 
           cod_unidade_pri, 
           qtde_aux, 
           REPLACE(valor_liquido_item,'.',',') AS 'valor liquido item',
           status_ctb, MONTH(data_v2) AS 'Mês', 
           DAY(data_v2) AS 'Dia'
    from vwMovtoEntrada
    Where COD_TIPO_MV IN ('811','T816','T819') -- aqui você declara todos os tipos de movimentação --
    Go

    Se esta query for processada e os dados que você deseja venham a ser apresentados, podemos evoluir mais um pouco informando a faixa de datas que devemos filtras, veja abaixo:

    SELECT chave_fato, cod_filial, cod_tipo_mv, 
           descricao, num_docto, cod_cli_for, 
           nome_cadastro, CONVERT(CHAR, data_v2, 103) AS 'data emissao', 
           REPLACE(valor_total,'.',',') AS 'Valor total',
           REPLACE(valor_liquido,'.',',') AS 'Valor liquido', 
           desc_divisao, 
           desc_produto_nf, 
           qtde_pri, 
           cod_unidade_pri, 
           qtde_aux, 
           REPLACE(valor_liquido_item,'.',',') AS 'valor liquido item',
           status_ctb, MONTH(data_v2) AS 'Mês', 
           DAY(data_v2) AS 'Dia'
    from vwMovtoEntrada
    Where COD_TIPO_MV IN ('811','T816','T816') -- aqui você declara todos os tipos de movimentação --
    AND DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' -- aqui você informa o intevalo de datas --
    Go

    Processe, verifique se o retorno esta sendo o que você espera, por último aplique a outra condição fazendo o filtro através da busca aproximada com uso no operador LIKE, veja abaixo:

    SELECT chave_fato, cod_filial, cod_tipo_mv, 
           descricao, num_docto, cod_cli_for, 
           nome_cadastro, CONVERT(CHAR, data_v2, 103) AS 'data emissao', 
           REPLACE(valor_total,'.',',') AS 'Valor total',
           REPLACE(valor_liquido,'.',',') AS 'Valor liquido', 
           desc_divisao, 
           desc_produto_nf, 
           qtde_pri, 
           cod_unidade_pri, 
           qtde_aux, 
           REPLACE(valor_liquido_item,'.',',') AS 'valor liquido item',
           status_ctb, MONTH(data_v2) AS 'Mês', 
           DAY(data_v2) AS 'Dia'
    from vwMovtoEntrada
    Where COD_TIPO_MV IN ('811','T816','T816') -- aqui você declara todos os tipos de movimentação --
    AND DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' -- aqui você informa o intevalo de datas --
    AND DESC_Divisao Like '%bov%' -- aqui você declara o valor que esta sendo pesquisa com base na expressão bov --
    Go

    Pois bem, realizei uma análise superficial da sua query, aparentemente você trabalhando com valores fixos e específicos, então não veja a necessidade de criar tantas condições.

    Eu particularmente gosto do aplicar no Where inicialmente as condições que vão trazer menos dados e conforme a necessidade vou deixando as condições que possam exigir mais processamento e retorno de dados para o final da minha query, o mesmo eu aplico no Order By, declaro inicialmente as colunas menores e no final as maiores, mas esta é a minha forma de elaborar o meu código, existem muitas discussões sobre isso.

    Pense que sempre temos que construir condições que possam ser consideradas pelo processador da query satisfatórias para se obter o dado da forma mais específica possível, um conceito bastante apresentado aqui nos fóruns chamada do SARgable, no qual a forma que montamos a expressão da condição estará totalmente satisfatória para se obter o dado que é passado no parâmetro, sendo que também o valor do dado deverá ajudar para que isso possa acontecer.


    Pedro Antonio Galvão Junior [MVP | MCC | MSTC | MIE | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados Relacional e Data Warehouse | Professor Universitário | @JuniorGalvaoMVP | http://pedrogalvaojunior.wordpress.com]



    terça-feira, 27 de agosto de 2019 13:29

Todas as Respostas

  • Flávio, tome cuidado com a definição do filtro para a coluna COD_TIPO_MV. Sugiro que utilize a forma

        COD_TIPO_MV in ('811', 'T816', 'T819')  

    ou, se quiser definir de forma individual, que defina a precedência dos operadores lógicos utilizando par de parênteses:

       AND (COD_TIPO_MV = '811' OR COD_TIPO_MV = 'T816' OR COD_TIPO_MV = 'T819') AND

    ---

    Seu código ficaria assim:

    -- código #1
    SELECT chave_fato, cod_filial, cod_tipo_mv, descricao, num_docto, cod_cli_for, nome_cadastro, 
           convert (char(10), data_v2, 103) as [data emissao], 
           replace (valor_total,'.',',') as [Valor total],
           replace (valor_liquido,'.',',') as [Valor liquido], 
           desc_divisao, desc_produto_nf, qtde_pri, cod_unidade_pri, qtde_aux,
           replace (valor_liquido_item,'.',',') as [valor liquido item],
           status_ctb, month (data_v2) as [Mês], day (data_v2) as Dia
      from vwMovtoEntrada
      where DATA_V2 between '2019-05-01' and '2019-05-31'
            and desc_divisao like '%bov%' 
            and COD_TIPO_MV in ('811', 'T816', 'T819');
    

      

    Lembre-se de marcar esta resposta se ela te ajudou a resolver o problema.


    José Diz     Belo Horizonte, MG - Brasil     [T-SQL performance tuning: Porto SQL]   [e-mail]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Editado José Diz terça-feira, 27 de agosto de 2019 13:22
    • Sugerido como Resposta José Diz quinta-feira, 26 de setembro de 2019 11:51
    terça-feira, 27 de agosto de 2019 13:17
  • ok. Se vc olhar a segunda opção, ela está da forma que você sugeriu, ou seja usando a argumentação IN.

    Agora qual a ordem correta dos parâmetros?

    Pq cada uma das opções me traz um resultado e não sei qual é o correto.

    terça-feira, 27 de agosto de 2019 13:22
  • Se vc olhar a segunda opção, ela está da forma que você sugeriu, ou seja usando a argumentação IN.

    Mas o filtro pela coluna desc_divisao está desativado na segunda opção!
         ... AND '2019-05-31 --AND desc_divisao LIKE '%BOV%'

      

    Agora qual a ordem correta dos parâmetros?

    Se você utilizar o IN para o filtro da coluna COD_TIPO_MV, a ordem não importa pois os demais operadores lógicos são do tipo AND. O otimizador de consultas (query optimizer) decidirá se algum será avaliado primeiro, considerando-se diversos fatores como existência de índice, estatísticas coletadas etc.

    ---

    Acrescentei o código #1 em minha resposta anterior.

    ---

    Sugestão de leitura: Precedência dos operadores.

     

    Lembre-se de marcar esta resposta se ela te ajudou a resolver o problema.


    José Diz     Belo Horizonte, MG - Brasil     [T-SQL performance tuning: Porto SQL]   [e-mail]


    Este conteúdo é fornecido sem garantias de qualquer tipo, seja expressa ou implícita.

    • Editado José Diz terça-feira, 27 de agosto de 2019 14:34
    terça-feira, 27 de agosto de 2019 13:27
  • Flávio,

    Bom, a questão de uma possível ordem correta, poderá variar de acordo com a necessidade, mas sabemos que sempre o operador AND exige que todas as condições sejam satisfatórias "verdadeiras" para o resultado ser apresentado, ao contrário do OR que no mínimo uma das condições e regras aplicadas sendo verdadeira algo será retornado.

    O que necessariamente esta query deverá retornar?

    Inicialmente eu aplicaria na cláusula query um filtro trazendo somente os COD_TIPO_MV, fazendo uso do operador IN, que por detrás dos panos faz a mesma coisa que OR, mas acaba se tornando mais legível para o entendimento do código, veja esta alteração:

    SELECT chave_fato, cod_filial, cod_tipo_mv, 
           descricao, num_docto, cod_cli_for, 
           nome_cadastro, CONVERT(CHAR, data_v2, 103) AS 'data emissao', 
           REPLACE(valor_total,'.',',') AS 'Valor total',
           REPLACE(valor_liquido,'.',',') AS 'Valor liquido', 
           desc_divisao, 
           desc_produto_nf, 
           qtde_pri, 
           cod_unidade_pri, 
           qtde_aux, 
           REPLACE(valor_liquido_item,'.',',') AS 'valor liquido item',
           status_ctb, MONTH(data_v2) AS 'Mês', 
           DAY(data_v2) AS 'Dia'
    from vwMovtoEntrada
    Where COD_TIPO_MV IN ('811','T816','T819') -- aqui você declara todos os tipos de movimentação --
    Go

    Se esta query for processada e os dados que você deseja venham a ser apresentados, podemos evoluir mais um pouco informando a faixa de datas que devemos filtras, veja abaixo:

    SELECT chave_fato, cod_filial, cod_tipo_mv, 
           descricao, num_docto, cod_cli_for, 
           nome_cadastro, CONVERT(CHAR, data_v2, 103) AS 'data emissao', 
           REPLACE(valor_total,'.',',') AS 'Valor total',
           REPLACE(valor_liquido,'.',',') AS 'Valor liquido', 
           desc_divisao, 
           desc_produto_nf, 
           qtde_pri, 
           cod_unidade_pri, 
           qtde_aux, 
           REPLACE(valor_liquido_item,'.',',') AS 'valor liquido item',
           status_ctb, MONTH(data_v2) AS 'Mês', 
           DAY(data_v2) AS 'Dia'
    from vwMovtoEntrada
    Where COD_TIPO_MV IN ('811','T816','T816') -- aqui você declara todos os tipos de movimentação --
    AND DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' -- aqui você informa o intevalo de datas --
    Go

    Processe, verifique se o retorno esta sendo o que você espera, por último aplique a outra condição fazendo o filtro através da busca aproximada com uso no operador LIKE, veja abaixo:

    SELECT chave_fato, cod_filial, cod_tipo_mv, 
           descricao, num_docto, cod_cli_for, 
           nome_cadastro, CONVERT(CHAR, data_v2, 103) AS 'data emissao', 
           REPLACE(valor_total,'.',',') AS 'Valor total',
           REPLACE(valor_liquido,'.',',') AS 'Valor liquido', 
           desc_divisao, 
           desc_produto_nf, 
           qtde_pri, 
           cod_unidade_pri, 
           qtde_aux, 
           REPLACE(valor_liquido_item,'.',',') AS 'valor liquido item',
           status_ctb, MONTH(data_v2) AS 'Mês', 
           DAY(data_v2) AS 'Dia'
    from vwMovtoEntrada
    Where COD_TIPO_MV IN ('811','T816','T816') -- aqui você declara todos os tipos de movimentação --
    AND DATA_V2 BETWEEN '2019-05-01' AND '2019-05-31' -- aqui você informa o intevalo de datas --
    AND DESC_Divisao Like '%bov%' -- aqui você declara o valor que esta sendo pesquisa com base na expressão bov --
    Go

    Pois bem, realizei uma análise superficial da sua query, aparentemente você trabalhando com valores fixos e específicos, então não veja a necessidade de criar tantas condições.

    Eu particularmente gosto do aplicar no Where inicialmente as condições que vão trazer menos dados e conforme a necessidade vou deixando as condições que possam exigir mais processamento e retorno de dados para o final da minha query, o mesmo eu aplico no Order By, declaro inicialmente as colunas menores e no final as maiores, mas esta é a minha forma de elaborar o meu código, existem muitas discussões sobre isso.

    Pense que sempre temos que construir condições que possam ser consideradas pelo processador da query satisfatórias para se obter o dado da forma mais específica possível, um conceito bastante apresentado aqui nos fóruns chamada do SARgable, no qual a forma que montamos a expressão da condição estará totalmente satisfatória para se obter o dado que é passado no parâmetro, sendo que também o valor do dado deverá ajudar para que isso possa acontecer.


    Pedro Antonio Galvão Junior [MVP | MCC | MSTC | MIE | Microsoft Evangelist | Microsoft Partner | Engenheiro de Softwares | Especialista em Banco de Dados Relacional e Data Warehouse | Professor Universitário | @JuniorGalvaoMVP | http://pedrogalvaojunior.wordpress.com]



    terça-feira, 27 de agosto de 2019 13:29
  • Prezado Flavio,

    Mas, realmente cada uma das opções da um resultado diferente devido a ordem dos operadores mudarem a precedência das queries.

    Combinar condições quando OR tem precedência (Visual Database Tools)

    Dê uma olhada no link acima para ver se lhe ajuda.

    O uso de parenteses resolve exatamente a definir quem vem 'primeiro' na leitura da querie.

    Você quem precisa entender qual a lógica que é necessário aplicar para o seu caso e a partir dai usar os parenteses deixando claro para o Sql Server qual a ordem que você quer que a querie seja lida.


    Jefferson Clyton Pereira da Silva - [MCSA | MCP | MCTS | MTA | Analista de Banco de Dados - Sql Server e Oracle ]

    • Sugerido como Resposta José Diz quinta-feira, 26 de setembro de 2019 11:51
    terça-feira, 27 de agosto de 2019 13:47