A curto-circuito da cláusula SQL WHERE é avaliada?

142

As expressões booleanas nas cláusulas SQL WHERE são avaliadas em curto-circuito ?

Por exemplo:

SELECT * 
FROM Table t 
WHERE @key IS NULL OR (@key IS NOT NULL AND @key = t.Key) 

Se @key IS NULL for avaliado como true, @key IS NOT NULL AND @key = t.Key é avaliada?

Se não, por que não?

Se sim, é garantido? Faz parte do ANSI SQL ou é específico do banco de dados?

Se for específico do banco de dados, SqlServer? Oráculo? MySQL?

Greg Dean
fonte
A cláusula @key NÃO É NULL redundante? A cláusula @key IS NULL no LHS cuida disso não?
gastador
10
@splender - depende da resposta para a pergunta
Greg Dean
@ Greg: Eu concordo com gastador. Não vejo a falta ou presença de curto-circuito fazendo alguma diferença. Se @key for NULL, @key = t.Key sempre retornará false, como NULL! = NULL (é por isso que usamos IS NULL, afinal).
25139 Michael Madsen
14
@ Michael e @ spender - A questão é: a segunda condição avalia ou não. O ponto da questão não é: essa instrução SQL específica é escrita com o menor número possível de caracteres. Em exemplos mais complicados, isso sem dúvida importaria, como se a cláusula where tivesse um curto-circuito, você poderia escrever expressões que de outra forma seriam errôneas.
Greg Dean
2
Curto-circuito implica avaliar as condições da esquerda para a direita. Dada uma condição, como WHERE a = 1 AND b = 2poderia ser eficiente para o mecanismo de banco de dados encontrar todas as linhas onde b = 2 primeiro, depois filtre onde a = 1. Se você pedir garantia, o otimizador se tornará inútil.
Salman A

Respostas:

72

ANSI SQL Draft 2003 5WD-01-Framework-2003-09.pdf

6.3.3.3 Ordem de avaliação da regra

[...]

Onde a precedência não é determinada pelos Formatos ou por parênteses, geralmente é realizada uma avaliação eficaz das expressões da esquerda para a direita. No entanto, depende da implementação se as expressões são realmente avaliadas da esquerda para a direita, principalmente quando operandos ou operadores podem causar condições a serem geradas ou se os resultados das expressões podem ser determinados sem avaliar completamente todas as partes da expressão.

Comunidade
fonte
4
Depende da implementação? Ótimo. É bom saber também. Pelo menos CASEestá em curto-circuito.
Dakab
3
Isso não significa que as avaliações da expressão estão mal definidas? "(0 = 0 OU NULL)", é sempre NULL se todos os termos forem avaliados, mas sempre verdadeiro se for avaliado da esquerda para a direita e em curto-circuito.
user48956
6
SQL é uma linguagem declarativa, basicamente expressa a lógica da computação sem descrever seu fluxo de controle; o que contradiz o estilo imperativo da avaliação de curto-circuito e suas conseqüências.
Jorge Garcia
Eu não tinha pensado dessa maneira @JorgeGarcia. Acho que a avaliação de curto-circuito força implicitamente uma ordem nas operações. Estou lutando com algum código em que isso possa estar na raiz de um problema sutil. Obrigado pela compreensão.
Carnot Antonio Romero
58

Pelo exposto, o curto-circuito não está realmente disponível.

Se você precisar, sugiro uma declaração de caso:

Where Case when Expr1 then Expr2 else Expr3 end = desiredResult

Expr1é sempre avaliado, mas apenas um Expr2e Expr3será avaliado por linha.

PMc
fonte
3
Depende da implementação do RDBMS, presumo. Pelo menos para o SQL Server, existe pelo menos uma exceção documentada para não mostrar esse comportamento (ou seja, curto-circuito); cf CASE (Transact-SQL) - Comentários . Eu citei este caso nesta resposta que dei sobre a pergunta Sql - ordem explícita das condições WHERE? .
TT.
1
Expressão de caso , não declaração.
jarlh
19

Eu acho que esse é um dos casos em que eu escreveria como se não tivesse um curto-circuito, por três razões.

  1. Porque para o MSSQL, isso não é resolvido olhando para o BOL no lugar óbvio, então, para mim, isso o torna canonicamente ambíguo.

  2. porque pelo menos sei que meu código funcionará. E o mais importante, os que vierem atrás de mim também, então eu não os prepararei para se preocupar com a mesma pergunta repetidamente.

  3. Escrevo com bastante frequência para vários produtos DBMS e não quero lembrar das diferenças para poder contorná-las facilmente.

dkretz
fonte
4
Ótima sugestão. Não responde à pergunta, mas é um ótimo ponto de vista pragmático. então +1
Greg Dean
12

Não acredito que seja garantido um curto-circuito no SQL Server (2005). O SQL Server executa sua consulta através do algoritmo de otimização que leva em consideração várias coisas (índices, estatísticas, tamanho da tabela, recursos etc.) para criar um plano de execução eficaz. Após essa avaliação, você não pode ter certeza de que sua lógica de curto-circuito está garantida.

Encontrei a mesma pergunta há algum tempo e minha pesquisa realmente não me deu uma resposta definitiva. Você pode escrever uma pequena consulta para dar uma prova de que funciona, mas pode ter certeza de que, à medida que a carga no banco de dados aumenta, as tabelas aumentam e as coisas são otimizadas e alteradas no banco de dados, essa conclusão será aguarde. Não pude e, portanto, errei por precaução e usei a cláusula CASE in WHERE para garantir um curto-circuito.

Mehmet Aras
fonte
7

Você deve ter em mente como os bancos de dados funcionam. Dada uma consulta parametrizada, o db cria um plano de execução com base nessa consulta sem os valores para os parâmetros. Essa consulta é usada sempre que a consulta é executada, independentemente dos valores reais fornecidos. Se o curto-circuito da consulta com determinados valores não importará para o plano de execução.

Logicalmind
fonte
6
importa para a velocidade de execução!
user4951
Só porque é assim que funciona atualmente, não significa que não possa ser alterado. Temos que separar modelo / semântica da implementação. Os planos de execução são implementados internamente para otimizar a execução de consultas ... e a semântica de curto-circuito não apenas contradiz a natureza declarativa do SQL, mas pode restringir essas otimizações. No entanto, se a semântica de avaliação de curto-circuito fosse suportada pelo DBMS, a implementação dos planos de execução mudaria para suportar essa semântica.
Jorge Garcia
3

Eu normalmente uso isso para parâmetros opcionais. É o mesmo que curto-circuito?

SELECT  [blah]
FROM    Emp
WHERE  ((@EmpID = -1) OR (@EmpID = EmpID))

Isso me dá a opção de passar -1 ou o que quer que seja responsável pela verificação opcional de um atributo. Às vezes, isso envolve a junção em várias tabelas ou, de preferência, uma visualização.

Muito útil, sem ter certeza do trabalho extra que ele oferece ao mecanismo db.

p.campbell
fonte
2

Para o SQL Server, acho que depende da versão, mas minha experiência com o SQL Server 2000 é que ele ainda avalia @key = t.Key mesmo quando @key é nulo. Em outras palavras, ele não produz um curto-circuito eficiente ao avaliar a cláusula WHERE.

Eu já vi pessoas recomendando uma estrutura como o seu exemplo como uma maneira de fazer uma consulta flexível na qual o usuário pode inserir ou não inserir vários critérios. Minha observação é que Key ainda está envolvido no plano de consulta quando @key é nulo e se Key é indexado, ele não usa o índice com eficiência.

Esse tipo de consulta flexível com critérios variados é provavelmente um caso em que o SQL criado dinamicamente é realmente o melhor caminho a percorrer. Se @key for nulo, você simplesmente não o inclui na consulta.

tetranz
fonte
2

Acabei de tropeçar nessa pergunta e já havia encontrado esta entrada no blog: http://rusanu.com/2009/09/13/on-sql-server-boolean-operator-short-circuit/

O servidor SQL é livre para otimizar uma consulta em qualquer lugar que ela achar conveniente; portanto, no exemplo dado na postagem do blog, você não pode confiar em curto-circuito.

No entanto, um CASE aparentemente está documentado para avaliar na ordem por escrito - verifique os comentários dessa publicação no blog.

Stolsvik
fonte
1

A principal característica da avaliação de curto-circuito é que ela para de avaliar a expressão assim que o resultado pode ser determinado. Isso significa que o restante da expressão pode ser ignorado porque o resultado será o mesmo, independentemente de ser avaliado ou não.

Operadores booleanos binários são alternativos, o que significa:

a AND b == b AND a
a OR  b == b OR  a
a XOR b == b XOR a

portanto, não há garantia na ordem da avaliação. A ordem da avaliação será determinada pelo otimizador de consulta.

Em linguagens com objetos, pode haver situações em que você pode escrever expressões booleanas que podem ser avaliadas apenas com a avaliação de curto-circuito. Sua construção de código de exemplo é frequentemente usada nesses idiomas (C #, Delphi, VB). Por exemplo:

if(someString == null | someString.Length == 0 )
  printf("no text in someString");

Este exemplo de C # causará exceção se someString == null porque será totalmente avaliado. Na avaliação de curto-circuito, funcionará sempre.

O SQL opera apenas em variáveis ​​escalares (sem objetos) que não podem ser inicializados, portanto, não há como gravar expressões booleanas que não possam ser avaliadas. Se você tiver algum valor NULL, qualquer comparação retornará false.

Isso significa que no SQL você não pode escrever expressões que sejam avaliadas diferentemente, dependendo do uso de curto-circuito ou avaliação completa.

Se a implementação do SQL usar avaliação de curto-circuito, esperamos que isso acelere a execução da consulta.

zendar
fonte
1
Sim, operadores booleanos são comutativos. Não acho que objetos (ou não) tenham algo a ver com isso.
Greg Dean
1

eu não sei sobre curto-circuito, mas eu escreveria isso como uma declaração if-else

if (@key is null)
begin

     SELECT * 
     FROM Table t 

end
else
begin

     SELECT * 
     FROM Table t 
     WHERE t.Key=@key

end

Além disso, as variáveis ​​devem sempre estar no lado direito da equação. isso o torna sargável.

http://en.wikipedia.org/wiki/Sargable

DForck42
fonte
1
Alguém pode corroborá-lo sobre as variáveis ​​à direita? Por alguma razão, acho difícil acreditar nisso.
Greg Dean
O searchoracle.techtarget.com/expert/KnowledgebaseAnswer/… não pode encontrar muito mais agora
#
Pelo que entendi o artigo. Ele está falando sobre funções em nomes de colunas que não são sargáveis. O que eu entendo. No entanto, acho que (A = @a) ou (@a = A) são importantes.
Greg Dean
Eu posso estar errado. pode ser uma boa pergunta se ela ainda não existir.
DForck42 28/04/09
1

Abaixo um teste rápido e sujo no SQL Server 2008 R2:

SELECT *
FROM table
WHERE 1=0
AND (function call to complex operation)

Isso retorna imediatamente sem registros. Tipo de comportamento de curto-circuito estava presente.

Então tentei o seguinte:

SELECT *
FROM table
WHERE (a field from table) < 0
AND (function call to complex operation)

sabendo que nenhum registro satisfaria esta condição:

(a field from table) < 0

Isso levou vários segundos, indicando que o comportamento do curto-circuito não existia mais e a operação complexa estava sendo avaliada para cada registro.

Espero que isso ajude caras.

Jorge
fonte
1
Meu palpite é que a primeira consulta foi "em curto-circuito" em tempo de compilação, antes da execução do plano realmente começar.
Louis Somers
1

Aqui está uma demonstração para provar que o MySQL executa um curto-circuito na cláusula WHERE :

http://rextester.com/GVE4880

Isso executa as seguintes consultas:

SELECT myint FROM mytable WHERE myint >= 3 OR myslowfunction('query #1', myint) = 1;
SELECT myint FROM mytable WHERE myslowfunction('query #2', myint) = 1 OR myint >= 3;

A única diferença entre eles é a ordem dos operandos na condição OR.

myslowfunctiondeliberadamente dorme por um segundo e tem o efeito colateral de adicionar uma entrada a uma tabela de logs sempre que for executada. Aqui estão os resultados do que é registrado ao executar as duas consultas acima:

myslowfunction called for query #1 with value 1
myslowfunction called for query #1 with value 2
myslowfunction called for query #2 with value 1
myslowfunction called for query #2 with value 2
myslowfunction called for query #2 with value 3
myslowfunction called for query #2 with value 4

A descrição acima mostra que uma função lenta é executada mais vezes quando aparece no lado esquerdo de uma condição OR quando o outro operando nem sempre é verdadeiro (devido a curto-circuito).

Steve Chambers
fonte
4
Hmm, o que você provavelmente quis dizer "Aqui está uma demonstração para provar que o MySQL executa um curto-circuito na cláusula WHERE neste exemplo em particular :"
TT.
1
Claro - é apenas uma prova de que isso pode acontecer.
Steve Chambers
0

Isso leva mais 4 segundos no analisador de consultas, portanto, pelo que posso ver, SE nem sequer está em curto ...

SET @ADate = NULL

IF (@ADate IS NOT NULL)
BEGIN
    INSERT INTO #ABla VALUES (1)
        (SELECT bla from a huge view)
END

Seria bom ter uma maneira garantida!

azulado
fonte
-2

É óbvio que o servidor MS Sql suporta a teoria de curto-circuito, para melhorar o desempenho, evitando verificações desnecessárias,

Exemplo de suporte:

SELECT 'TEST'
WHERE 1 = 'A'

SELECT 'TEST'
WHERE 1 = 1 OR 1 = 'A'

Aqui, o primeiro exemplo resultaria no erro 'Falha na conversão ao converter o valor varchar' A 'para o tipo de dados int'.

Enquanto a segunda é executada facilmente, a condição 1 = 1 é avaliada como TRUE e, portanto, a segunda condição não é executada.

Além disso

SELECT 'TEST'
WHERE 1 = 0 OR 1 = 'A'

aqui a primeira condição seria avaliada como falsa e, portanto, o DBMS passaria para a segunda condição e, novamente, você receberá o erro de conversão como no exemplo acima.

OBSERVAÇÃO: ESCREVA A CONDIÇÃO ERRÔNICA APENAS PARA REALIZAR O TEMPO QUE A CONDIÇÃO É EXECUTADA OU CURTA CIRCUITO SE RESULTADOS QUERIDOS EM ERRO SIGNIFICAM A CONDIÇÃO EXECUTADA E CURTA CIRCUITO DE OUTRA FORMA.

EXPLICAÇÃO SIMPLES

Considerar,

WHERE 1 = 1 OR 2 = 2

pois a primeira condição está sendo avaliada como TRUE , não faz sentido avaliar a segunda condição porque sua avaliação em qualquer valor não afetaria o resultado, portanto, é uma boa oportunidade para o Sql Server economizar tempo de execução de consulta ignorando a verificação ou avaliação desnecessária da condição .

no caso de "OR", se a primeira condição for avaliada como VERDADEIRA, toda a cadeia conectada por "OR" será considerada como verdadeira sem avaliar outras.

condition1 OR condition2 OR ..... OR conditionN

se a condição1 for avaliada como verdadeira, descanse todas as condições até que a condiçãoN seja ignorada. Em palavras generalizadas na determinação do primeiro TRUE , todas as outras condições vinculadas por OR seriam ignoradas.

Considere a segunda condição

WHERE 1 = 0 AND 1 = 1

como a primeira condição está sendo avaliada para FALSE avaliada não faz sentido avaliar a segunda condição, porque sua avaliação em qualquer valor não afetaria o resultado; novamente, é uma boa oportunidade para o Sql Server economizar o tempo de execução da consulta ignorando a verificação ou avaliação desnecessária da condição .

no caso de "AND", se a primeira condição for avaliada como FALSE, toda a cadeia conectada com o "AND" seria considerada avaliada como FALSE sem avaliar outras.

condition1 AND condition2 AND ..... conditionN

se a condição1 for avaliada como FALSE , descanse todas as condições até que a condiçãoN seja ignorada. Em palavras generalizadas na determinação do primeiro FALSE , todas as outras condições vinculadas por AND seriam ignoradas.

Portanto, um programador sábio sempre deve programar a cadeia de condições de uma maneira que, menos dispendiosa ou a maioria das condições de eliminação, seja avaliada em primeiro lugar ou altere a condição de uma maneira que possa tirar o máximo benefício de curto-circuito

RkHirpara
fonte
Motivo com voto negativo: sempre teste as coisas em um servidor real com dados realistas. Parece que meu comentário anterior foi comido.
Jasmine