Prestazioni di a = 0 e b = 0 e… z = 0 vs a + b + c + d = 0


20

Questa è una domanda semplice per la quale non riesco a trovare la risposta.

In termini di prestazioni, se ho una WHEREclausola del tipo a=0 and b=0 and ... z=0, otterrei prestazioni se sostituissi quella condizione con a+b+...+z=0?

In altre parole, c'è qualche guadagno in termini di prestazioni sostituendo quanto segue

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

Con

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

So che può dipendere dagli indici, ma a questo scopo, diciamo solo che non esistono indici. L'operatore aritmetico (+) ha prestazioni migliori di un operatore logico "OR" o "AND"?

Ho l'impressione che l'aggiunta funzioni meglio di più condizioni con AND o OR.

Risultati del test

Su una tabella di 4,2 milioni di righe

Restituzione di righe Dove A = 0 B = 0 e C = 0 -> 351748 Righe

L'aggiunta (A + B + C = 0) ha richiesto 5 secondi mentre le condizioni logiche A = 0 e B = 0 e C = 0 sono durate 11 secondi.

D'altro canto

Restituzione di righe Dove A <> 0 B <> 0 o C <> 0 -> 3829750 Righe 58 secondi

Restituzione di righe Dove F65 + F67 + f64 <> 0 -> 3829750 Righe 57 secondi

Per l'OR, sembra che non ci siano differenze significative.

Sono d'accordo con gbn:

Se A è -1 e B è 1, A + B = 0 ma A = 0 e B = 0 è falso

e con AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... Anche se ti aspetti solo valori positivi, se la colonna accetta valori negativi, dovresti presumere che potresti incontrarne uno

I risultati sono molto impressionanti, come pensavo, sembra che l'aggiunta sia molto più rapida degli operatori logici.

A = Float, B = Denaro e C = Float. La query utilizzata è come mostrato. Nel mio caso, tutti sono numeri positivi. Nessun indice È logico nella mia mente che l'aggiunta sarebbe più rapida delle condizioni logiche!


Sono booleani? Di quante colonne stai parlando di 4 (negli esempi) o 26 (nel titolo)? Fa la differenza. Quale versione di SQL Server? Dove entrano in gioco FLOAT e DENARO? Quante righe stiamo presumendo? Questa domanda ha molti fattori.
Evan Carroll,

@Evan Carroll Non sono booleani, sono numeri non indicizzati (int, float, money, ecc.). Indipendentemente dalla versione SQL (SQL2012 e successive), dal numero di righe o colonne, la domanda era scoprire quale operatore ha prestazioni migliori - operatori logici vs operatori aritmetici. Come puoi vedere, Max Vernon dimostra perfettamente la teoria con i suoi esempi.
JohnG

Risposte:


46

Nella tua domanda, descrivi dettagliatamente alcuni test che hai preparato dove "dimostri" che l'opzione di aggiunta è più rapida rispetto al confronto delle colonne discrete. Sospetto che la tua metodologia di prova possa essere viziata in diversi modi, come hanno accennato @gbn e @srutzky.

Innanzitutto, è necessario assicurarsi di non testare SQL Server Management Studio (o qualsiasi client si stia utilizzando). Ad esempio, se stai eseguendo un SELECT *da una tabella con 3 milioni di righe, stai principalmente testando la capacità di SSMS di estrarre righe da SQL Server e renderle sullo schermo. Stai molto meglio usare qualcosa di simile SELECT COUNT(1)che annulla la necessità di tirare milioni di righe attraverso la rete e renderle sullo schermo.

In secondo luogo, è necessario conoscere la cache dei dati di SQL Server. In genere, testiamo la velocità di lettura dei dati dall'archiviazione e dell'elaborazione di tali dati da una cache fredda (ovvero i buffer di SQL Server sono vuoti). Occasionalmente, ha senso eseguire tutti i test con una cache calda, ma è necessario affrontare esplicitamente i test con questo in mente.

Per un test della cache fredda, è necessario eseguire CHECKPOINTe DBCC DROPCLEANBUFFERSprima di ogni esecuzione del test.

Per il test che hai posto nella tua domanda, ho creato il seguente banco di prova:

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;

Ciò restituisce un conteggio di 260.144.641 sulla mia macchina.

Per testare il metodo "addizione", eseguo:

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;

La scheda messaggi mostra:

Tabella '#SomeTest'. Conteggio scansioni 3, letture logiche 1322661, letture fisiche 0, letture avanti 1313877, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 49047 ms, tempo trascorso = 173451 ms.

Per il test "colonne discrete":

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;

di nuovo, dalla scheda messaggi:

Tabella '#SomeTest'. Conteggio scansioni 3, letture logiche 1322661, letture fisiche 0, letture read-ahead 1322661, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 8938 ms, tempo trascorso = 162581 ms.

Dalle statistiche sopra puoi vedere la seconda variante, con le colonne discrete rispetto a 0, il tempo trascorso è di circa 10 secondi più breve e il tempo della CPU è circa 6 volte inferiore. Le lunghe durate nei miei test sopra sono principalmente il risultato della lettura di molte righe dal disco. Se si abbassa il numero di righe a 3 milioni, i rapporti restano più o meno gli stessi ma i tempi trascorsi diminuiscono notevolmente, poiché l'I / O del disco ha un effetto molto minore.

Con il metodo "Aggiunta":

Tabella '#SomeTest'. Conteggio scansioni 3, letture logiche 15255, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 499 ms, tempo trascorso = 256 ms.

Con il metodo "colonne discrete":

Tabella '#SomeTest'. Conteggio scansioni 3, letture logiche 15255, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 94 ms, tempo trascorso = 53 ms.

Cosa farà davvero la differenza per questo test? Un indice appropriato, come ad esempio:

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

Il metodo "addizione":

Tabella '#SomeTest'. Conteggio scansioni 3, letture logiche 14235, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 546 ms, tempo trascorso = 314 ms.

Il metodo "colonne discrete":

Tabella '#SomeTest'. Conteggio scansioni 1, letture logiche 3, letture fisiche 0, letture avanti 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 0 ms, tempo trascorso = 0 ms.

Il piano di esecuzione per ogni query (con l'indice sopra in atto) è abbastanza eloquente.

Il metodo "addizione", che deve eseguire una scansione dell'intero indice:

inserisci qui la descrizione dell'immagine

e il metodo "colonne discrete", che può cercare la prima riga dell'indice in cui la colonna dell'indice principale Aè zero:

inserisci qui la descrizione dell'immagine


24

Supponiamo che tu abbia un indice su A, B, C e D. Potrebbe essere filtrato anche.

È più probabile che utilizzi l'indice rispetto all'aggiunta.

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

In altre notizie, se A è -1 e B è 1, A+B=0è vero ma A=0 and B=0è falso.


7

(Si noti che questa risposta è stata inviata prima che qualsiasi test venisse notato nella domanda: il testo della domanda terminava appena sopra la sezione Risultati del test .)

Immagino che le ANDcondizioni separate sarebbero preferite poiché l'ottimizzatore avrebbe maggiori probabilità di cortocircuitare l'operazione se una sola di esse non è uguale a 0, senza dover prima effettuare un calcolo.

Tuttavia, poiché si tratta di una questione di prestazioni, è necessario innanzitutto impostare un test per determinare la risposta sul proprio hardware. Segnala quei risultati, mostrando il tuo codice di test e chiedi ad altri di esaminarlo per accertarti che sia stato un buon test. Potrebbero esserci altri fattori degni di considerazione a cui non hai pensato.


3

Alcuni ragionamenti generali, se non hai indici a portata di mano, non penso che avrà molta importanza quale delle due soluzioni che scegli, entrambe funzioneranno male. Se invece hai un indice su una o più delle colonne nel predicato, probabilmente la prima funzionerà meglio della seconda, poiché la seconda probabilmente non sarà in grado di utilizzare l'indice.

Le disgiunzioni (OR) in generale si comportano peggio delle congiunzioni (AND), ma anche se hai una domanda con disgiunzioni inserirò i miei soldi nel primo.


2

Questa è una domanda semplice

No non lo è. Questa (sorta di) domanda è ciò che affligge molti DBA e sviluppatori di software giorno dopo giorno, ed è quasi banale.

per cui non riesco a trovare la risposta.

Sì, non lo farai. Almeno non una risposta generale. Prima di tutto, dipenderà enormemente da quale RDBMS stai usando (OK, stai usando , ma comunque). Può anche cambiare quando passi da una versione del tuo RDBMS alla successiva.

Quindi, può dipendere da qualsiasi quantità di altri piccoli dettagli, ad esempio il modo in cui il database memorizza i dati, se sono presenti sotto-selezioni / join che confondono il problema per l'ottimizzatore del piano, ecc. L'ottimizzatore potrebbe fornire piani di esecuzione diversi a seconda su quante righe hai ...

Fare un test nel mondo reale è di solito l'unico modo utile per risolvere domande come questa. Inoltre, qualsiasi guadagno ottenuto da ottimizzazioni "arcane" come questa è di solito inghiottito dieci volte dalla scelta intelligente degli indici, quindi non mi preoccuperei di passare troppo tempo su di esso, prima che un uso degli indici fosse davvero escluso.


0

Questo può essere ovvio, ma se le colonne sono INT, allora a+b+cpotrebbe essere uguale a zero anche quando nessuna di esse è effettivamente zero. Stai testando due cose diverse!


Ho appena realizzato che @gbn l'ha menzionato nella sua risposta.
Ross Presser,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.