Desempenho de a = 0 eb = 0 e… z = 0 vs a + b + c + d = 0

20

Esta é uma pergunta simples para a qual não consigo encontrar a resposta.

Em termos de desempenho, se eu tiver uma WHEREcláusula como a=0 and b=0 and ... z=0: Eu obteria algum desempenho se substituísse essa condição por a+b+...+z=0?

Em outras palavras, há algum ganho de desempenho substituindo o seguinte

Select * 
From MyTable 
Where A=0 and B=0 and C=0 and D=0...

Com

Select * 
From MyTable 
Where A+B+C+D=0...

Eu sei que isso pode depender de índices, mas, para esse propósito, digamos que não existem índices. O operador aritmético (+) tem um desempenho melhor que um operador lógico "OR" ou "AND"?

Estou com a impressão de que a adição tem um desempenho melhor do que várias condições com ANDs ou ORs.

Resultado dos testes

Em uma tabela de 4,2 milhões de linhas

Retornando linhas em que A = 0 B = 0 e C = 0 -> 351748 Linhas

A adição (A + B + C = 0) levou 5 segundos, enquanto as condições lógicas A = 0 e B = 0 e C = 0 levaram 11 segundos.

Por outro lado

Retornando linhas em que A <> 0 B <> 0 ou C <> 0 -> 3829750 Linhas 58 segundos

Retornando linhas em que F65 + F67 + f64 <> 0 -> 3829750 Linhas 57 segundos

Para a sala de cirurgia, parece que não há diferença significativa.

Eu concordo com o gbn:

Se A é -1 e B é 1, A + B = 0, mas A = 0 e B = 0 é falso

e com AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... Mesmo que você espere apenas valores positivos, se a coluna aceitar valores negativos, você deve assumir que pode encontrar um

Os resultados são muito impressionantes, como pensei, parece que a adição é muito mais rápida que os operadores lógicos.

A = Flutuar, B = Dinheiro e C = Flutuar. A consulta usada é como mostrado. No meu caso, todos são números positivos. Sem índices. É lógico em minha mente que a adição seria mais rápida que as condições lógicas!

JohnG
fonte
São booleanos? De quantas colunas você está falando de 4 (nos exemplos) ou 26 (no título)? Faz diferença. Qual versão do SQL Server? Onde o FLOAT e o DINHEIRO entram em jogo? Quantas linhas estamos presumindo? Esta questão tem muitos fatores.
Evan Carroll
@Evan Carroll Eles não são booleanos, são números não indexados (int, float, money, etc). Independentemente da versão do SQL (SQL2012 e superior), o número de linhas ou colunas, a questão era descobrir qual operador executa melhor os operadores lógicos versus aritméticos. Como você pode ver, Max Vernon demonstra perfeitamente a teoria com seus exemplos.
JohnG 6/02

Respostas:

46

Na sua pergunta, você detalha alguns testes que preparou onde "prova" que a opção de adição é mais rápida do que comparar as colunas discretas. Eu suspeito que sua metodologia de teste pode ter falhas de várias maneiras, como @gbn e @srutzky mencionaram.

Primeiro, você precisa garantir que não está testando o SQL Server Management Studio (ou qualquer outro cliente que esteja usando). Por exemplo, se você estiver executando um SELECT *de uma tabela com 3 milhões de linhas, estará testando principalmente a capacidade do SSMS de extrair linhas do SQL Server e renderizá-las na tela. É muito melhor usar algo como o SELECT COUNT(1)que nega a necessidade de puxar milhões de linhas pela rede e renderizá-las na tela.

Segundo, você precisa estar ciente do cache de dados do SQL Server. Normalmente, testamos a velocidade de leitura de dados do armazenamento e do processamento desses dados, a partir de um cache frio (por exemplo, os buffers do SQL Server estão vazios). Ocasionalmente, faz sentido fazer todos os seus testes com um cache quente, mas você precisa abordá-los explicitamente com isso em mente.

Para um teste de cache frio, você precisa executar CHECKPOINTe DBCC DROPCLEANBUFFERSantes de cada execução do teste.

Para o teste que você perguntou na sua pergunta, criei o seguinte banco de testes:

IF COALESCE(OBJECT_ID('tempdb..#SomeTest'), 0) <> 0
BEGIN
    DROP TABLE #SomeTest;
END
CREATE TABLE #SomeTest
(
    TestID INT NOT NULL
        PRIMARY KEY 
        IDENTITY(1,1)
    , A INT NOT NULL
    , B FLOAT NOT NULL
    , C MONEY NOT NULL
    , D BIGINT NOT NULL
);

INSERT INTO #SomeTest (A, B, C, D)
SELECT o1.object_id, o2.object_id, o3.object_id, o4.object_id
FROM sys.objects o1
    , sys.objects o2
    , sys.objects o3
    , sys.objects o4;

SELECT COUNT(1) 
FROM #SomeTest;

Isso retorna uma contagem de 260.144.641 na minha máquina.

Para testar o método "adição", eu corro:

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE (st.A + st.B + st.C + st.D) = 0;
GO
SET STATISTICS IO, TIME OFF;

A guia de mensagens mostra:

Tabela '#SomeTest'. Contagem de varredura 3, leituras lógicas 1322661, leituras físicas 0, leituras antecipadas 1313877, leituras lógicas lob 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob.

Tempos de execução do SQL Server: tempo de CPU = 49047 ms, tempo decorrido = 173451 ms.

Para o teste "colunas discretas":

CHECKPOINT 5;
DBCC FREEPROCCACHE;
DBCC DROPCLEANBUFFERS;

SET STATISTICS IO, TIME ON;
GO
SELECT COUNT(1)
FROM #SomeTest st
WHERE st.A = 0
    AND st.B = 0
    AND st.C = 0
    AND st.D = 0;
GO

SET STATISTICS IO, TIME OFF;

novamente, na guia Mensagens:

Tabela '#SomeTest'. Contagem de varredura 3, leituras lógicas 1322661, leituras físicas 0, leituras de leitura antecipada 1322661, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada 0.

Tempos de execução do SQL Server: tempo de CPU = 8938 ms, tempo decorrido = 162581 ms.

Nas estatísticas acima, você pode ver a segunda variante, com as colunas discretas comparadas com 0, o tempo decorrido é cerca de 10 segundos mais curto e o tempo da CPU é cerca de 6 vezes menor. As longas durações nos meus testes acima são principalmente o resultado da leitura de muitas linhas do disco. Se você reduzir o número de linhas para 3 milhões, verá que as proporções permanecem as mesmas, mas os tempos decorridos caem visivelmente, pois a E / S do disco tem muito menos efeito.

Com o método "Adição":

Tabela '#SomeTest'. Contagem de varredura 3, leituras lógicas 15255, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.

Tempos de execução do SQL Server: tempo de CPU = 499 ms, tempo decorrido = 256 ms.

Com o método "colunas discretas":

Tabela '#SomeTest'. Contagem de varredura 3, leituras lógicas 15255, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.

Tempos de execução do SQL Server: tempo de CPU = 94 ms, tempo decorrido = 53 ms.

O que fará uma diferença realmente grande para este teste? Um índice apropriado, como:

CREATE INDEX IX_SomeTest ON #SomeTest(A, B, C, D);

O método "adição":

Tabela '#SomeTest'. Contagem de varredura 3, leituras lógicas 14235, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.

Tempos de execução do SQL Server: tempo de CPU = 546 ms, tempo decorrido = 314 ms.

O método "colunas discretas":

Tabela '#SomeTest'. Contagem de varreduras 1, leituras lógicas 3, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de read-ahead de lob 0.

Tempos de execução do SQL Server: tempo de CPU = 0 ms, tempo decorrido = 0 ms.

O plano de execução para cada consulta (com o índice acima no local) é bastante revelador.

O método "adição", que deve executar uma verificação de todo o índice:

insira a descrição da imagem aqui

e o método "colunas discretas", que pode procurar a primeira linha do índice em que a coluna principal do índice Aé zero:

insira a descrição da imagem aqui

Max Vernon
fonte
24

Digamos que você tenha um índice em A, B, C e D. Também pode ser filtrado.

É mais provável que você use o índice do que a adição.

Where A=0 and B=0 and C=0 and D=0

Em outras notícias, se A é -1 e B é 1, A+B=0é verdade, mas A=0 and B=0é falsa.

gbn
fonte
7

(Observe que esta resposta foi enviada antes de qualquer teste ser observado na pergunta: o texto da pergunta terminou logo acima da seção de resultados do teste .)

Eu acho que as ANDcondições separadas seriam preferidas, já que o otimizador teria mais probabilidade de causar um curto-circuito na operação se um único deles não fosse igual a 0, sem a necessidade de fazer um cálculo primeiro.

Ainda assim, como se trata de uma questão de desempenho, você deve primeiro configurar um teste para determinar a resposta no seu hardware. Relate esses resultados, mostrando seu código de teste e solicite que outras pessoas o examinem para garantir que foi um bom teste. Pode haver outros fatores dignos de consideração nos quais você não pensou.

Solomon Rutzky
fonte
3

Algum raciocínio geral: se você não possui nenhum índice em mãos, acho que não importará muito qual das duas soluções que você escolher, ambas terão um desempenho ruim. Se, por outro lado, você tiver um índice em uma ou mais das colunas do predicado, a primeira provavelmente terá um desempenho melhor que a segunda, pois a segunda provavelmente não poderá utilizar o (s) índice (s).

Disjunções (OR) em geral têm desempenho pior que conjunções (AND), mas mesmo se você tiver uma consulta com disjunções, colocarei meu dinheiro na primeira.

Lennart
fonte
2

Esta é uma pergunta simples

Não não é. Essa (tipo de) pergunta é o que atormenta muitos DBAs e desenvolvedores de software todos os dias, e é praticamente trivial.

que parece que não consigo encontrar a resposta.

Sim, você não vai. Pelo menos não uma resposta geral. Primeiro de tudo, isso dependerá enormemente de qual RDBMS você está usando (OK, você está usando , mas ainda assim). Pode até mudar quando você passa de uma versão do seu RDBMS para a seguinte.

Então, pode depender de qualquer quantidade de outros pequenos detalhes, por exemplo, como o seu banco de dados armazena os dados, se você tiver sub-seleções / junções que confundem o problema do otimizador de plano etc. O otimizador pode fornecer planos de execução diferentes, dependendo em quantas linhas você tem ...

Fazer um teste no mundo real geralmente é a única maneira útil de resolver perguntas como essa. Além disso, todos os ganhos obtidos por otimizações "misteriosas" como essa geralmente são ingeridos dez vezes por uma escolha inteligente de índices; portanto, eu não me incomodaria em gastar muito tempo com isso antes que um uso de índices seja realmente descartado.

AnoE
fonte
0

Isso pode ser óbvio, mas se as colunas forem INT, então a+b+cpoderá ser igual a zero, mesmo quando nenhuma delas for zero. Você está testando duas coisas diferentes!

Ross Presser
fonte
Acabei de perceber que @gbn mencionou isso em sua resposta.
Ross Presser