Performance de a = 0 et b = 0 et… z = 0 vs a + b + c + d = 0

20

C'est une question simple pour laquelle je n'arrive pas à trouver la réponse.

En termes de performances, si j'ai une WHEREclause telle que a=0 and b=0 and ... z=0, gagnerais-je des performances si je remplaçais cette condition par a+b+...+z=0?

En d'autres termes, y a-t-il un gain de performances en remplaçant les éléments suivants

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

Avec

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

Je sais que cela peut dépendre des index, mais à cet effet, disons simplement qu'aucun index n'existe. L'opérateur arithmétique (+) fonctionne-t-il mieux qu'un opérateur logique "OU" ou "ET"?

J'ai l'impression que l'ajout fonctionne mieux que plusieurs conditions avec des ET ou des OU.

Résultats de test

Sur un tableau de 4,2 millions de lignes

Lignes de retour Où A = 0 B = 0 et C = 0 -> 351748 Lignes

L'addition (A + B + C = 0) a pris 5 secondes tandis que les conditions logiques A = 0 et B = 0 et C = 0 ont pris 11 secondes.

D'autre part

Lignes de retour Où A <> 0 B <> 0 ou C <> 0 -> 3829750 Lignes 58 secondes

Lignes de retour Où F65 + F67 + f64 <> 0 -> 3829750 Lignes 57 secondes

Pour la RO, il semble qu'il n'y ait pas de différence significative.

Je suis d'accord avec gbn:

Si A est -1 et B est 1, A + B = 0 mais A = 0 et B = 0 est faux

et avec AMtwo:

ABS (A) + ABS (B) + ABS (C) + ABS (D) ... Même si vous n'attendez que des valeurs positives, si la colonne accepte des valeurs négatives, vous devez supposer que vous pourriez en rencontrer un

Les résultats sont très impressionnants, comme je le pensais, il semble que l'addition soit beaucoup plus rapide que les opérateurs logiques.

A = flottant, B = argent et C = flottant. La requête utilisée est la suivante. Dans mon cas, tous sont des nombres positifs. Pas d'index. Il est logique dans mon esprit que l'addition soit plus rapide que les conditions logiques!

JohnG
la source
Sont-ils booléens? De combien de colonnes parlez-vous 4 (dans les exemples), ou 26 (dans le titre)? Cela fait une différence. Quelle version de SQL Server? Où FLOAT et MONEY entrent-ils en jeu? Combien de lignes supposons-nous? Cette question a une tonne de facteurs.
Evan Carroll
@Evan Carroll Ce ne sont pas des booléens, ce sont des nombres non indexés (int, float, money, etc.). Quelle que soit la version SQL (SQL2012 et versions ultérieures), le nombre de lignes ou de colonnes, la question était de savoir quel opérateur est le plus performant - les opérateurs logiques et arithmétiques. Comme vous pouvez le voir, Max Vernon illustre parfaitement la théorie avec ses exemples.
JohnG

Réponses:

46

Dans votre question, vous détaillez certains tests que vous avez préparés où vous "prouvez" que l'option d'ajout est plus rapide que la comparaison des colonnes discrètes. Je soupçonne que votre méthodologie de test peut être défectueuse de plusieurs manières, comme l'ont fait référence à @gbn et @srutzky.

Tout d'abord, vous devez vous assurer que vous ne testez pas SQL Server Management Studio (ou le client que vous utilisez). Par exemple, si vous exécutez une SELECT *table à partir de 3 millions de lignes, vous testez principalement la capacité de SSMS à extraire des lignes de SQL Server et à les afficher à l'écran. Il vaut mieux utiliser quelque chose comme SELECT COUNT(1)qui annule la nécessité de tirer des millions de lignes sur le réseau et de les afficher à l'écran.

Deuxièmement, vous devez connaître le cache de données de SQL Server. En règle générale, nous testons la vitesse de lecture des données du stockage et du traitement de ces données à partir d'un cache froid (c'est-à-dire que les tampons de SQL Server sont vides). Parfois, il est logique de faire tous vos tests avec un cache chaud, mais vous devez aborder vos tests de manière explicite dans cet esprit.

Pour un test de cache froid, vous devez exécuter CHECKPOINTet DBCC DROPCLEANBUFFERSavant chaque exécution du test.

Pour le test que vous avez demandé dans votre question, j'ai créé le banc d'essai suivant:

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;

Cela renvoie un nombre de 260 144 641 sur ma machine.

Pour tester la méthode "addition", je lance:

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;

L'onglet messages affiche:

Tableau '#SomeTest'. Nombre de balayages 3, lectures logiques 1322661, lectures physiques 0, lectures en lecture anticipée 1313877, lob lectures logiques 0, lob lectures physiques 0, lob lectures anticipées en lecture 0.

Temps d'exécution SQL Server: temps CPU = 49047 ms, temps écoulé = 173451 ms.

Pour le test "colonnes discrètes":

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;

encore une fois, dans l'onglet messages:

Tableau '#SomeTest'. Nombre de balayages 3, lectures logiques 1322661, lectures physiques 0, lectures anticipées 1322661, lob lectures logiques 0, lob lectures physiques 0, lob lectures anticipées lisent 0.

Temps d'exécution SQL Server: temps CPU = 8938 ms, temps écoulé = 162581 ms.

D'après les statistiques ci-dessus, vous pouvez voir la deuxième variante, avec les colonnes discrètes par rapport à 0, le temps écoulé est environ 10 secondes plus court et le temps CPU est environ 6 fois moins. Les longues durées de mes tests ci-dessus sont principalement le résultat de la lecture d'un grand nombre de lignes à partir du disque. Si vous réduisez le nombre de lignes à 3 millions, vous voyez que les ratios restent à peu près les mêmes, mais les temps écoulés chutent sensiblement, car les E / S disque ont beaucoup moins d'effet.

Avec la méthode "Addition":

Tableau '#SomeTest'. Nombre de balayages 3, lectures logiques 15255, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 499 ms, temps écoulé = 256 ms.

Avec la méthode des "colonnes discrètes":

Tableau '#SomeTest'. Nombre de balayages 3, lectures logiques 15255, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 94 ms, temps écoulé = 53 ms.

Qu'est-ce qui fera vraiment une grande différence pour ce test? Un index approprié, tel que:

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

La méthode "d'addition":

Tableau '#SomeTest'. Nombre de balayages 3, lectures logiques 14235, lectures physiques 0, lectures anticipées 0, lob lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 546 ms, temps écoulé = 314 ms.

La méthode des "colonnes discrètes":

Tableau '#SomeTest'. Nombre de balayages 1, lectures logiques 3, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 0 ms, temps écoulé = 0 ms.

Le plan d'exécution pour chaque requête (avec l'index ci-dessus en place) est assez révélateur.

La méthode "addition", qui doit effectuer un scan de l'index entier:

entrez la description de l'image ici

et la méthode des "colonnes discrètes", qui peut rechercher la première ligne de l'index où la colonne d'index de tête A, est nulle:

entrez la description de l'image ici

Max Vernon
la source
24

Disons que vous avez un index sur A, B, C et D. Peut également être filtré.

Cela est plus susceptible d'utiliser l'index que l'addition.

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

Dans d'autres nouvelles, si A est -1 et B est 1, A+B=0c'est vrai mais A=0 and B=0c'est faux.

gbn
la source
7

(Veuillez noter que cette réponse a été soumise avant que tout test ne soit noté dans la Question: le texte de la Question se terminait juste au-dessus de la section Résultats du test .)

Je suppose que les ANDconditions distinctes seraient préférées car l'optimiseur serait plus susceptible de court-circuiter le fonctionnement si un seul d'entre eux n'est pas égal à 0, sans avoir besoin de faire un calcul au préalable.

Néanmoins, comme il s'agit d'une question de performances, vous devez d'abord configurer un test pour déterminer la réponse sur votre matériel. Signalez ces résultats, montrez votre code de test et demandez à d'autres de le vérifier pour vous assurer qu'il s'agit d'un bon test. Il peut y avoir d'autres facteurs dignes de considération auxquels vous n'avez pas pensé.

Solomon Rutzky
la source
3

Un raisonnement général, si vous n'avez pas d'index à portée de main, je ne pense pas que ce soit très important laquelle des deux solutions que vous choisissez, les deux fonctionneront mal. Si vous avez en revanche un index sur une ou plusieurs des colonnes du prédicat, la première sera probablement plus performante que la seconde, car la seconde ne pourra probablement pas utiliser le ou les index.

Les disjonctions (OR) fonctionnent généralement moins bien que les conjonctions (AND), mais même si vous avez une requête avec des disjonctions, je mettrai mon argent sur la première.

Lennart
la source
2

C'est une question simple

Non, ça ne l'est pas. Cette (sorte de) question est ce qui afflige de nombreux administrateurs de bases de données et développeurs de logiciels jour après jour, et c'est tout sauf trivial.

que je n'arrive pas à trouver la réponse.

Oui, tu ne le feras pas. Du moins pas une réponse générale. Tout d'abord, cela dépendra énormément du SGBDR que vous utilisez (OK, vous utilisez , mais quand même). Il peut même changer lorsque vous passez d'une version de votre SGBDR à la suivante.

Ensuite, cela peut dépendre de n'importe quelle quantité d'autres petits détails, par exemple comment votre base de données stocke les données, si vous avez des sous-sélections / jointures qui confondent le problème pour l'optimiseur de plan, etc. L'optimiseur peut vous donner différents plans d'exécution selon sur le nombre de lignes que vous avez ...

Faire un test du monde réel est généralement le seul moyen utile de résoudre des questions comme celle-ci. De plus, tout gain obtenu grâce à des optimisations "mystérieuses" comme celle-ci est généralement décuplé par un choix intelligent d'index, donc je ne prendrais pas la peine de passer trop de temps dessus, avant qu'une utilisation des index ne soit vraiment exclue.

AnoE
la source
0

Cela peut être évident, mais si les colonnes le sont INT, alors elles a+b+cpourraient être égales à zéro même lorsqu'aucune d'entre elles n'est réellement nulle. Vous testez deux choses différentes!

Ross Presser
la source
Je viens de réaliser que @gbn l'a mentionné dans sa réponse.
Ross Presser