Warum sind mehrere COUNT mit CASE schneller als eine SUMME?

14

Ich wollte wissen, welcher der beiden folgenden Ansätze schneller ist:

1) Drei COUNT:

 SELECT Approved = (SELECT COUNT(*) FROM dbo.Claims d
                  WHERE d.Status = 'Approved'),
        Valid    = (SELECT COUNT(*) FROM dbo.Claims d
                    WHERE d.Status = 'Valid'),
        Reject   = (SELECT COUNT(*) FROM dbo.Claims d
                    WHERE d.Status = 'Reject')

2) SUMmit FROM-Klausel:

SELECT  Approved = SUM(CASE WHEN Status = 'Approved' THEN 1 ELSE 0 END),
        Valid    = SUM(CASE WHEN Status = 'Valid'    THEN 1 ELSE 0 END),
        Reject   = SUM(CASE WHEN Status = 'Reject'   THEN 1 ELSE 0 END)
FROM dbo.Claims c;

Ich war überrascht, dass der Unterschied so groß ist. Die erste Abfrage mit drei Unterabfragen gibt das Ergebnis sofort zurück, während die zweite Abfrage SUM18 Sekunden benötigt.

Claimsist eine Ansicht, die aus einer Tabelle mit ~ 18 Millionen Zeilen auswählt. In der FK-Spalte befindet sich ein Index zu der ClaimStatusTabelle, die den Statusnamen enthält.

Warum macht es so einen großen Unterschied, ob ich benutze COUNToder SUM?

Ausführungspläne:

Insgesamt gibt es 12 Status. Diese drei Status gehören zu 7% aller Zeilen.


Dies ist die aktuelle Ansicht, ich bin nicht sicher, ob es relevant ist:

CREATE VIEW [dbo].[Claims]
AS
SELECT 
   mu.Marketunitname AS MarketUnit, 
   c.Countryname     AS Country, 
   gsp.Gspname       AS GSP, 
   gsp.Wcmskeynumber AS GspNumber, 
   sl.Slname         AS SL, 
   sl.Wcmskeynumber  AS SlNumber, 
   m.Modelname       AS Model, 
   m.Salesname       AS [Model-Salesname], 
   s.Claimstatusname AS [Status], 
   d.Work_order      AS [Work Order], 
   d.Ssn_number      AS IMEI, 
   d.Ssn_out, 
   Remarks, 
   d.Claimnumber     AS [Claim-Number], 
   d.Rma_number      AS [RMA-Number], 
   dbo.ToShortDateString(d.Received_Date, 1) AS [Received Date], 
   Iddata, 
   Fisl, 
   Fimodel, 
   Ficlaimstatus 
FROM Tabdata AS d 
   INNER JOIN Locsl AS sl 
           ON d.Fisl = sl.Idsl 
   INNER JOIN Locgsp AS gsp 
           ON sl.Figsp = gsp.Idgsp 
   INNER JOIN Loccountry AS c 
           ON gsp.Ficountry = c.Idcountry 
   INNER JOIN Locmarketunit AS mu 
           ON c.Fimarketunit = mu.Idmarketunit 
   INNER JOIN Modmodel AS m 
           ON d.Fimodel = m.Idmodel 
   INNER JOIN Dimclaimstatus AS s 
           ON d.Ficlaimstatus = s.Idclaimstatus 
   INNER JOIN Tdefproducttype 
           ON d.Fiproducttype = Tdefproducttype.Idproducttype 
   LEFT OUTER JOIN Tdefservicelevel 
                ON d.Fimaxservicelevel = Tdefservicelevel.Idservicelevel 
   LEFT OUTER JOIN Tdefactioncode AS ac 
                ON d.Fimaxactioncode = ac.Idactioncode 
Tim Schmelter
quelle
Es sieht so aus, als ob beide Links auf die COUNTVersion des Plans verweisen . Können Sie sich dergleichen auf die bearbeiten SUMVersion auf den richtigen Plan zu zeigen?
Geoff Patterson
Wie ist das Verhältnis der Zeilen mit diesen drei Zuständen zu den Zeilen mit anderen Zuständen?
Max Vernon
1
@MaxVernon: Ja, natürlich habe ich zu viele Nullen gesehen, du hast recht. Lassen Sie mich meine Kommentare löschen. Ja, es gibt 16,7 Millionen Zeilen in einem anderen Status. Die meisten sind Authorized.
Tim Schmelter
2
Ich würde schätzen, dass der zweite Plan 12 Mal den gesamten Tisch scannen muss (das ist, was angezeigt wird). Dies ist wahrscheinlich darauf zurückzuführen, dass die Prädikate nicht in den Scan gedrückt werden können. Was ist Leistung wie wenn Sie hinzufügen , WHERE c.Status = 'Approved' or c.Status = 'Valid' or c.status = 'Reject'auf die SUMVariante.
Max Vernon
@MaxVernon: Insgesamt gibt es zwölf Status. Für mich ist das kein wirkliches Problem, aber ich war sehr überrascht, dass der Optimierer damit nicht fertig wird. Ich sollte wirklich an meinen Fähigkeiten zur Analyse des Ausführungsplans arbeiten. Mach eine Antwort darauf. Was ist Ihre Annahme, warum kann SQL-Server nicht nur drei Status prüfen?
Tim Schmelter

Antworten:

19

Die COUNT(*)Version kann den Index, den Sie in der Statusspalte haben, einfach einmal für jeden ausgewählten Status durchsuchen, während die SUM(...)Version den Index zwölfmal durchsuchen muss (die Gesamtzahl der eindeutigen Statustypen).

Die dreimalige Suche nach einem Index ist eindeutig schneller als die zwölfmalige Suche.

Der erste Plan erfordert eine Speicherzuweisung von 238 MB, während der zweite Plan eine Speicherzuweisung von 650 MB erfordert. Es kann sein , dass die größere Speichergewährungs nicht sofort gefüllt werden kann, so dass die Abfrage , die viel langsamer.

Ändern Sie die zweite Abfrage in:

SELECT  Approved = SUM(CASE WHEN Status = 'Approved' THEN 1 ELSE 0 END),
        Valid    = SUM(CASE WHEN Status = 'Valid'    THEN 1 ELSE 0 END),
        Reject   = SUM(CASE WHEN Status = 'Reject'   THEN 1 ELSE 0 END)
FROM dbo.Claims c
WHERE c.Status = 'Approved'
    OR c.Status = 'Valid'
    OR c.Status = 'Reject';

Auf diese Weise kann das Abfrageoptimierungsprogramm 75% der Indexsuchen eliminieren und sollte sowohl zu einer geringeren erforderlichen Speicherzuweisung als auch zu geringeren E / A-Anforderungen und einer schnelleren Zeit bis zum Ergebnis führen.

Das SUM(CASE WHEN ...)Konstrukt verhindert im Wesentlichen, dass der Abfrageoptimierer die StatusPrädikate in den Indexsuchabschnitt des Plans drückt .

Max Vernon
quelle
Schöner Fang mit der Erinnerung. Ich habe festgestellt, dass alle meine 32 GB derzeit verwendet werden (nur 300 MB frei). Bearbeiten Allerdings habe ich etwas Speicher freigegeben. Das Ergebnis ist das gleiche
Tim Schmelter
Möglicherweise möchten Sie die max server memoryOption prüfen - sie sollte auf den richtigen Wert für Ihr System konfiguriert sein. Sie können sich diese Frage und die Antworten ansehen, um Einzelheiten dazu zu erfahren.
Max Vernon
1
Leider wird dieser Server nicht nur für die Datenbank, sondern auch für einen SSAS-Cube und einige Tools (einschließlich Intranet-Web-App) verwendet. Aber ich habe bereits 12 GB als Maximum zugewiesen.
Tim Schmelter