Was ist schneller, SUM (CASE) oder CTE PIVOT?


8

Es gibt zwei Arten von Möglichkeiten, a auszuführen PIVOT. Vor der PIVOTEinführung von SQL Server 2005 haben die meisten Benutzer Folgendes getan:

SELECT RateID
              SUM(CASE WHEN RateItemTypeID = 1 THEN UnitPrice ELSE 0 END),
              SUM(CASE WHEN RateItemTypeID = 2 THEN UnitPrice ELSE 0 END),
              SUM(CASE WHEN RateItemTypeID = 3 THEN UnitPrice ELSE 0 END)
              FROM rate_item WHERE _WhereClause_
              GROUP BY RateID

Später, als 2005 eingeführt wurde PIVOT, wurde dies:

   SELECT RateID, [1], [2], [3]
          FROM PertinentRates -- PertinentRates is a CTE with WHERE clause applied
          PIVOT (SUM(UnitPrice) FOR RateItemTypeID IN ([1], [2], [3])) PVT)

In SQL Server 2005, 2008 R2, 2012 und 2014 (den Versionen von SQL Server, mit denen ich gearbeitet habe PIVOT) war es meiner Erfahrung nach immer schneller als SUM(CASE)oder in einigen Fällen gleich schnell. Gibt es Beispiele, wo PIVOTlangsamer ist?

Ich kann die DDL nicht angeben, da dies ein Beispiel aus meiner Arbeit ist. Aber der Tisch ist ziemlich einfach. Im PIVOTBeispiel wird aus einem CTE SUM(CASE)gezeichnet, während der direkt aus der Tabelle gezeichnet wird. Aber der SUM(CASE)führt die gleiche Zeichnung aus dem CTE aus.

In meinem Arbeitsbeispiel PIVOTkommt das in 10 Sekunden zurück, während das SUM(CASE)in 14 zurückkommt. Es muss eindeutig etwas anderes unter der Decke sein. Die Pläne sind die gleichen, jeweils 50% der Gesamtzahl. im Abfrageanalysator PIVOTkonvertiert SUM(CASE). Doch SUM(CASE)kommt nie wieder in weniger als 13 Sekunden und PIVOTnie in mehr als 11 Sekunden kommt zurück.

Ich habe versucht, sie hin und her auszuführen, es spielt keine Rolle, in welcher Reihenfolge sie ausgeführt werden. Wenn ich sie beide aus einem kalten Cache ausführe, dauern beide länger, sind aber PIVOTimmer noch schneller, 12 gegenüber 17 Sekunden. Kann nicht auf einem zweiten Server reproduziert werden, aber dieser ist erheblich besser. Es sind jeweils 5 Sekunden mit geringfügigen Abweichungen. PIVOTist ein bisschen besser, aber prozentual hat es nicht den gleichen Vorteil wie auf dem ersten Server.

Die E / A-Statistiken sind wie der Abfrageplan zwischen beiden identisch. Das ist seltsam, ich hatte erwartet, dass verschiedene E / A-Statistiken angezeigt werden, obwohl ich sie für dieses spezielle Beispiel noch nie angesehen habe.

Antworten:


22

Gibt es Beispiele, wo PIVOTlangsamer ist?

Dies ist in einfachen Fällen unwahrscheinlich . Wie Itzik Ben-Gan in seinem SQL Server Pro-Artikel feststellt , Pivoting Data beim Betrachten des Plans für eine PIVOTAbfrage (Hervorhebung hinzugefügt):

Abbildung 3 zeigt den Plan für die PIVOTAbfrage. Wie Sie sehen, ist dieser Plan dem der Standardlösung sehr ähnlich - so sehr, dass Sie, wenn Sie sich die Eigenschaften des Aggregatoperators unter Definierte Werte ansehen, feststellen, dass SQL Server CASEhinter den Kulissen Ausdrücke erstellt hat:

...
[Expr1022] = Scalar Operator(SUM(CASE WHEN [InsideTSQL2008].[Sales].[Orders].[shipcity]=N'Barcelona' THEN [InsideTSQL2008].[Sales].[Orders].[freight] ELSE NULL END)) ...

Vor diesem Hintergrund sollten Sie nicht erwarten, dass die auf dem PIVOTBediener basierende Lösung eine bessere Leistung als die Standardlösung erbringt. Der Hauptvorteil des PIVOTBedieners im Moment ist, dass er weniger ausführlich ist.

Für erweiterte Schwenkanforderungen, die von der (nicht standardmäßigen) PIVOTSyntax nicht direkt unterstützt werden, sind Problemumgehungen erforderlich. Diese können im Vergleich zu verschiedenen Faktoren, einschließlich des Kenntnisstands des Implementierers, zu einer schlechteren Leistung führen oder auch nichtCASE .

Beispiele für diese problematischen Fälle werden in Itziks Artikel behandelt und auch in Robert Sheldons Simple Talk-Artikel " Fragen zum Schwenken von Daten in SQL Server, den Sie zu schüchtern gestellt haben" ausführlich erläutert .

Meine Erfahrung ist, dass PIVOTund Agg(CASE...erzeugt extrem Ähnliche Pläne mit extrem engen Leistungseigenschaften , wenn beide optimal geschrieben werden. Mein üblicher Rat ist, Abfragen mit der für Sie natürlichsten Syntax zu schreiben und nur dann zu versuchen, sie neu zu schreiben, wenn die Leistung nicht akzeptabel ist.

Interna

Der SQL Server-Abfrageprozessor verfügt über einen integrierten logischen Pivot- Operator ( LogOp_Pivot). Daher ist es möglicherweise nicht ganz richtig zu sagen, dass SQL Server Pivots in Aggregate und Fallausdrücke umschreibt, zumindest wenn es sich um Parsing- und Kompilierungsaktivitäten handelt Platz vor der kostenbasierten Optimierung (triviale Pläne sind für Pivot-Abfragen nicht verfügbar).

Andererseits ist es wahr, dass der Optimierer einen Abfragebaum, der enthält, nur über die Explorationsregel implementieren kann . Diese Regel wird zu einem normalen gruppierten Aggregat ( ) mit zugehörigen skalaren Ausdrücken erweitert. Wenn diese Regel deaktiviert ist, können Pivot-Abfragen nicht kompiliert werden.LogOp_PivotExpandPivotLogOp_PivotLogOp_GbAgg

In der Praxis können wir dann sagen, dass Pivots immer (irgendwann) als Aggregate und skalare Ausdrücke "umgeschrieben" werden, bevor ein ausführbarer Plan erstellt werden kann.

Auf jeden Fall wird das Ergebnis des Umschreibens in LogOp_GbAggin die physischen Operatoren konvertiert, die für einen ausführbaren Plan durch die regulären Implementierungsregeln für gruppierte Aggregate GbAggToHS(Hash) oder GbAggToStrm(Stream) benötigt werden.

Nebenbei bemerkt, der Grund, warum 'manuelle Pivots' (Aggregate für Fallausdrücke) einen zusätzlichen Berechnungsskalar unter dem Aggregat haben, ist, dass die Fallausdrücke während der Projektnormalisierung (eine frühe Phase der Kompilierung) auf die Blattebene des Abfragebaums verschoben werden. vor kostenbasierter Optimierung).

Abfragen, die die PIVOTSyntax verwenden, haben dies nicht, da die Ausdrücke erst erstellt werden, wenn sie ExpandPivotwährend der kostenbasierten Optimierung ausgeführt werden. Zum (früheren) Zeitpunkt, zu dem die Projektnormalisierung ausgeführt wird, enthält der Abfragebaum noch LogOp_PivotElemente, sodass keine Projektionen nach unten verschoben werden müssen und die Fallausdrücke normalerweise im Hash- oder Stream-Aggregat landen.

Das Vermeiden des Berechnungsskalars ist normalerweise nicht vorteilhaft, da ab SQL Server 2005 die Ausdrucksauswertung normalerweise verschoben wird, bis das Ergebnis von einem späteren Operator benötigt wird. In diesem Fall wird die Auswertung der Fallausdrücke verschoben, bis das Aggregat (Hash oder Stream) dies erfordert.


12

Wiederholen der Tests von Kreuztabellen und Drehpunkten, Teil 1 - Konvertieren von Zeilen in Spalten - Von Jeff Moden, 06.08.2010 (erstmals veröffentlicht: 19.08.2008) zum Rextester

Leider kann ich im Rextester nicht auf Statistiken für E / A-, Zeit- oder Ausführungspläne zugreifen, aber es hat den einzigartigen Vorteil, eine gemeinsame Testumgebung zu sein, an der jeder hier basteln und untersuchen kann. Mir ist klar, dass dies immer noch zu wünschen übrig lässt, wenn es darum geht, genau zu untersuchen, was gerade passiert, aber ich würde sagen, dass die Möglichkeit, eine Testumgebung gemeinsam zu nutzen, eine wichtige Facette für diese Diskussion ist.


Einfach

Rextester: http://rextester.com/BAZMGJ69528

Dieser wurde für @MartinSmith hinzugefügt und obwohl die Abfragen aus demselben Artikel stammen, war er in den ursprünglichen Tests nicht wie folgt:

create table #timer (what varchar(64), ended datetime);
insert into #timer values ('Start',getdate());
go

SELECT TOP 400000 --<<Look!  Change this number for testing different size tables
        RowNum       = IDENTITY(INT,1,1),
        Company      = CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65),
        Amount       = CAST(ABS(CHECKSUM(NEWID()))%1000000/100.0 AS MONEY),
        Quantity     = ABS(CHECKSUM(NEWID()))%50000+1,
        Date         = CAST(RAND(CHECKSUM(NEWID()))*3653.0+36524.0 AS DATETIME),
        Year         = CAST(NULL AS SMALLINT),
        Quarter      = CAST(NULL AS TINYINT)
   INTO #SomeTable3
   FROM Master.sys.SysColumns t1
  CROSS JOIN
        Master.sys.SysColumns t2 

--===== Fill in the Year and Quarter columns from the Date column
 UPDATE #SomeTable3
    SET Year    = DATEPART(yy,Date),
        Quarter = DATEPART(qq,Date)

--===== A table is not properly formed unless a Primary Key has been assigned
     -- Takes about 1 second to execute.
  ALTER TABLE #SomeTable3
        ADD PRIMARY KEY CLUSTERED (RowNum)
CREATE NONCLUSTERED INDEX IX_#SomeTable3_CoverYear 
    ON dbo.#SomeTable3 (Year)
       INCLUDE (Amount, Quantity, Quarter) 

create statistics syear on #sometable3(year) with fullscan, norecompute;
create statistics syearquarter on #sometable3(year,quarter) with fullscan, norecompute;
GO
insert into #timer values ('Finished Loading Test Data',getdate());
go
--===== Simple Pivot 
 SELECT Year, 
        COALESCE([1],0) AS [1st Qtr],
        COALESCE([2],0) AS [2nd Qtr],
        COALESCE([3],0) AS [3rd Qtr],
        COALESCE([4],0) AS [4th Qtr],
        COALESCE([1],0) + COALESCE([2] ,0) + COALESCE([3],0) + COALESCE([4],0) AS Total
   into #SimplePivot_prep
   FROM (SELECT Year, Quarter,Amount FROM #SomeTable3)  AS src 
  PIVOT (SUM(Amount) FOR Quarter IN ([1],[2],[3],[4])) AS pvt 
go
--===== Simple Cross Tab
 SELECT Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount ELSE 0 END) AS [1st Qtr],
        SUM(CASE WHEN Quarter = 2 THEN Amount ELSE 0 END) AS [2nd Qtr],
        SUM(CASE WHEN Quarter = 3 THEN Amount ELSE 0 END) AS [3rd Qtr],
        SUM(CASE WHEN Quarter = 4 THEN Amount ELSE 0 END) AS [4th Qtr],
        SUM(Amount) AS Total
   into #simpleCrossTab_prep
   FROM #SomeTable3
  GROUP BY Year
go
--insert into #timer values ('Simple Cross Tab',getdate());
go
--=====--
insert into #timer values ('Finished Prep',getdate());
go
--=====--
--===== Simple Pivot
 SELECT Year, 
        COALESCE([1],0) AS [1st Qtr],
        COALESCE([2],0) AS [2nd Qtr],
        COALESCE([3],0) AS [3rd Qtr],
        COALESCE([4],0) AS [4th Qtr],
        COALESCE([1],0) + COALESCE([2] ,0) + COALESCE([3],0) + COALESCE([4],0) AS Total
   into #SimplePivot
   FROM (SELECT Year, Quarter,Amount FROM #SomeTable3)  AS src 
  PIVOT (SUM(Amount) FOR Quarter IN ([1],[2],[3],[4])) AS pvt 
go
insert into #timer values ('Simple Pivot',getdate());
go
--=====--
--===== Simple Cross Tab
 SELECT Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount ELSE 0 END) AS [1st Qtr],
        SUM(CASE WHEN Quarter = 2 THEN Amount ELSE 0 END) AS [2nd Qtr],
        SUM(CASE WHEN Quarter = 3 THEN Amount ELSE 0 END) AS [3rd Qtr],
        SUM(CASE WHEN Quarter = 4 THEN Amount ELSE 0 END) AS [4th Qtr],
        SUM(Amount) AS Total
   into #simpleCrossTab
   FROM #SomeTable3
  GROUP BY Year
go 
insert into #timer values ('Simple Cross Tab',getdate());
go
--=====--
select 
    o.what
  , started=isnull(convert(varchar(30),x.ended),o.ended)
  , ended=convert(varchar(30),o.ended)
  , DurationInMs=datediff(millisecond,x.ended,o.ended)
from #timer o
  outer apply (select top 1 ended from #timer i where i.ended < o.ended order by i.ended desc) as x

kehrt zurück:

+----------------------------+---------------------+---------------------+--------------+
|            what            |       started       |        ended        | DurationInMs |
+----------------------------+---------------------+---------------------+--------------+
| Start                      | Feb 19 2017  7:13PM | Feb 19 2017  7:13PM | NULL         |
| Finished Loading Test Data | Feb 19 2017  7:13PM | Feb 19 2017  7:13PM | 7210         |
| Finished Prep              | Feb 19 2017  7:13PM | Feb 19 2017  7:13PM | 700          |
| Simple Pivot               | Feb 19 2017  7:13PM | Feb 19 2017  7:13PM | 340          |
| Simple Cross Tab           | Feb 19 2017  7:13PM | Feb 19 2017  7:13PM | 386          |
+----------------------------+---------------------+---------------------+--------------+

Der Rest sind alle Testbeschränkungen in der pivotSyntax, bei denen eine einzelne Kreuztabellenabfrage das erreichen kann, was mehrere pivots erfordern würde .

Normal

Rextester: http://rextester.com/UVZE87903

create table #timer (what varchar(64), ended datetime);
insert into #timer values ('Start',getdate());
go

 SELECT TOP 300000 --<<Look!  Change this number for testing different size tables
        RowNum       = IDENTITY(INT,1,1),
        Company      = CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65),
        Amount       = CAST(ABS(CHECKSUM(NEWID()))%1000000/100.0 AS MONEY),
        Quantity     = ABS(CHECKSUM(NEWID()))%50000+1,
        Date         = CAST(RAND(CHECKSUM(NEWID()))*3653.0+36524.0 AS DATETIME),
        Year         = CAST(NULL AS SMALLINT),
        Quarter      = CAST(NULL AS TINYINT)
   INTO #SomeTable3
   FROM Master.sys.SysColumns t1
  CROSS JOIN
        Master.sys.SysColumns t2 

--===== Fill in the Year and Quarter columns from the Date column
 UPDATE #SomeTable3
    SET Year    = DATEPART(yy,Date),
        Quarter = DATEPART(qq,Date)

--===== A table is not properly formed unless a Primary Key has been assigned
     -- Takes about 1 second to execute.
  ALTER TABLE #SomeTable3
        ADD PRIMARY KEY CLUSTERED (RowNum)
CREATE NONCLUSTERED INDEX IX_#SomeTable3_Cover1 
    ON dbo.#SomeTable3 (Company, Year)
       INCLUDE (Amount, Quantity, Quarter) 

create statistics scompanyyear on #sometable3(company, year) with fullscan, norecompute;
GO
insert into #timer values ('Finished Loading Test Data',getdate());
go
--=====--
--===== "Normal" Pivot
 SELECT amt.Company,
        amt.Year,
        COALESCE(amt.[1],0) AS Q1Amt,
        COALESCE(qty.[1],0) AS Q1Qty,
        COALESCE(amt.[2],0) AS Q2Amt,
        COALESCE(qty.[2],0) AS Q2Qty,
        COALESCE(amt.[3],0) AS Q3Amt,
        COALESCE(qty.[3],0) AS Q3Qty,
        COALESCE(amt.[4],0) AS Q4Amt,
        COALESCE(qty.[4],0) AS Q5Qty,
        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,
        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty
   into #NormalPivot_prep
   FROM (SELECT Company, Year, Quarter, Amount FROM #SomeTable3) t1
        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt
  INNER JOIN 
        (SELECT Company, Year, Quarter, Quantity FROM #SomeTable3) t2
        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty
     ON qty.Company = amt.Company 
    AND qty.Year    = amt.Year         
  ORDER BY amt.Company, amt.Year
go
--insert into #timer values ('Finished Normal Pivot',getdate());
go
--=====--
--===== "Normal" Cross Tab
 SELECT Company,
        Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,
        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,
        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,
        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,
        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,
        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,
        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,
        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,
        SUM(Amount)   AS TotalAmt,
        SUM(Quantity) AS TotalQty
   into #NormalCrossTab_prep
   FROM #SomeTable3
  GROUP BY Company, Year
  ORDER BY Company, Year
go
--insert into #timer values ('Finished Normal Cross Tab',getdate());
insert into #timer values ('Finished Prep',getdate());
go
--=====--
--===== "Normal" Pivot
 SELECT amt.Company,
        amt.Year,
        COALESCE(amt.[1],0) AS Q1Amt,
        COALESCE(qty.[1],0) AS Q1Qty,
        COALESCE(amt.[2],0) AS Q2Amt,
        COALESCE(qty.[2],0) AS Q2Qty,
        COALESCE(amt.[3],0) AS Q3Amt,
        COALESCE(qty.[3],0) AS Q3Qty,
        COALESCE(amt.[4],0) AS Q4Amt,
        COALESCE(qty.[4],0) AS Q5Qty,
        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,
        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty
   into #NormalPivot
   FROM (SELECT Company, Year, Quarter, Amount FROM #SomeTable3) t1
        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt
  INNER JOIN 
        (SELECT Company, Year, Quarter, Quantity FROM #SomeTable3) t2
        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty
     ON qty.Company = amt.Company 
    AND qty.Year    = amt.Year         
  ORDER BY amt.Company, amt.Year
go
insert into #timer values ('Finished Normal Pivot',getdate());
go
--=====--
--===== "Normal" Cross Tab
 SELECT Company,
        Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,
        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,
        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,
        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,
        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,
        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,
        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,
        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,
        SUM(Amount)   AS TotalAmt,
        SUM(Quantity) AS TotalQty
   into #NormalCrossTab
   FROM #SomeTable3
  GROUP BY Company, Year
  ORDER BY Company, Year
go
insert into #timer values ('Finished Normal Cross Tab',getdate());
go
--=====--
select 
    o.what
  , started=isnull(convert(varchar(30),x.ended),o.ended)
  , ended=convert(varchar(30),o.ended)
  , DurationInMs=datediff(millisecond,x.ended,o.ended)
from #timer o
  outer apply (select top 1 ended from #timer i where i.ended < o.ended order by i.ended desc) as x

kehrt zurück:

+----------------------------+---------------------+---------------------+--------------+
|            what            |       started       |        ended        | DurationInMs |
+----------------------------+---------------------+---------------------+--------------+
| Start                      | Feb 19 2017  7:19PM | Feb 19 2017  7:19PM | NULL         |
| Finished Loading Test Data | Feb 19 2017  7:19PM | Feb 19 2017  7:19PM | 5260         |
| Finished Prep              | Feb 19 2017  7:19PM | Feb 19 2017  7:19PM | 1003         |
| Finished Normal Pivot      | Feb 19 2017  7:19PM | Feb 19 2017  7:19PM | 550          |
| Finished Normal Cross Tab  | Feb 19 2017  7:19PM | Feb 19 2017  7:19PM | 513          |
+----------------------------+---------------------+---------------------+--------------+

"Voraggregiert"

Rextester: http://rextester.com/WBGUYR51251

create table #timer (what varchar(64), ended datetime);
insert into #timer values ('Start',getdate());
go
SELECT TOP 300000 --<<Look!  Change this number for testing different size tables
        RowNum       = IDENTITY(INT,1,1),
        Company      = CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65),
        Amount       = CAST(ABS(CHECKSUM(NEWID()))%1000000/100.0 AS MONEY),
        Quantity     = ABS(CHECKSUM(NEWID()))%50000+1,
        Date         = CAST(RAND(CHECKSUM(NEWID()))*3653.0+36524.0 AS DATETIME),
        Year         = CAST(NULL AS SMALLINT),
        Quarter      = CAST(NULL AS TINYINT)
   INTO #SomeTable3
   FROM Master.sys.SysColumns t1
  CROSS JOIN
        Master.sys.SysColumns t2 

--===== Fill in the Year and Quarter columns from the Date column
 UPDATE #SomeTable3
    SET Year    = DATEPART(yy,Date),
        Quarter = DATEPART(qq,Date)

--===== A table is not properly formed unless a Primary Key has been assigned
     -- Takes about 1 second to execute.
  ALTER TABLE #SomeTable3
        ADD PRIMARY KEY CLUSTERED (RowNum)
CREATE NONCLUSTERED INDEX IX_#SomeTable3_Cover1 
    ON dbo.#SomeTable3 (Company, Year)
       INCLUDE (Amount, Quantity, Quarter) 

create statistics scompanyyear on #sometable3(company, year) with fullscan, norecompute;
GO
insert into #timer values ('Finished Loading Test Data',getdate());
go
--=====--
--===== "Pre-aggregated" Pivot
SELECT amt.Company,
        amt.Year,
        COALESCE(amt.[1],0) AS Q1Amt,
        COALESCE(qty.[1],0) AS Q1Qty,
        COALESCE(amt.[2],0) AS Q2Amt,
        COALESCE(qty.[2],0) AS Q2Qty,
        COALESCE(amt.[3],0) AS Q3Amt,
        COALESCE(qty.[3],0) AS Q3Qty,
        COALESCE(amt.[4],0) AS Q4Amt,
        COALESCE(qty.[4],0) AS Q5Qty,
        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,
        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty
   into #preA_Pivot_prep
   FROM (SELECT Company, Year, Quarter, SUM(Amount) AS Amount FROM #SomeTable3 GROUP BY Company, Year, Quarter) t1
        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt
  INNER JOIN 
        (SELECT Company, Year, Quarter, SUM(Quantity) AS Quantity FROM #SomeTable3 GROUP BY Company, Year, Quarter) t2
        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty
     ON qty.Company = amt.Company 
    AND qty.Year    = amt.Year         
  ORDER BY amt.Company, amt.Year
go
--insert into #timer values ('Finished "Pre-aggregated" Pivot',getdate());
go
--=====--
--===== "Pre-aggregated" Cross Tab
SELECT Company,
        Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,
        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,
        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,
        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,
        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,
        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,
        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,
        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,
        SUM(Amount)   AS TotalAmt,
        SUM(Quantity) AS TotalQty
   into #preA_CrossTab_prep
   FROM (SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity
           FROM #SomeTable3 GROUP BY Company,Year,Quarter) d
  GROUP BY Company, Year
  ORDER BY Company, Year
go
--insert into #timer values ('Finished "Pre-aggregated" Cross Tab',getdate());
go
--=====--
insert into #timer values ('Finished Prep',getdate());
--=====--
--===== "Pre-aggregated" Pivot
SELECT amt.Company,
        amt.Year,
        COALESCE(amt.[1],0) AS Q1Amt,
        COALESCE(qty.[1],0) AS Q1Qty,
        COALESCE(amt.[2],0) AS Q2Amt,
        COALESCE(qty.[2],0) AS Q2Qty,
        COALESCE(amt.[3],0) AS Q3Amt,
        COALESCE(qty.[3],0) AS Q3Qty,
        COALESCE(amt.[4],0) AS Q4Amt,
        COALESCE(qty.[4],0) AS Q5Qty,
        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,
        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty
   into #preA_Pivot
   FROM (SELECT Company, Year, Quarter, SUM(Amount) AS Amount FROM #SomeTable3 GROUP BY Company, Year, Quarter) t1
        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt
  INNER JOIN 
        (SELECT Company, Year, Quarter, SUM(Quantity) AS Quantity FROM #SomeTable3 GROUP BY Company, Year, Quarter) t2
        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty
     ON qty.Company = amt.Company 
    AND qty.Year    = amt.Year         
  ORDER BY amt.Company, amt.Year
go
insert into #timer values ('Finished "Pre-aggregated" Pivot',getdate());
go
--=====--
--===== "Pre-aggregated" Cross Tab
SELECT Company,
        Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,
        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,
        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,
        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,
        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,
        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,
        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,
        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,
        SUM(Amount)   AS TotalAmt,
        SUM(Quantity) AS TotalQty
   into #preA_CrossTab
   FROM (SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity
           FROM #SomeTable3 GROUP BY Company,Year,Quarter) d
  GROUP BY Company, Year
  ORDER BY Company, Year
go
insert into #timer values ('Finished "Pre-aggregated" Cross Tab',getdate());
go
--=====--
select 
    o.what
  , started=isnull(convert(varchar(30),x.ended),o.ended)
  , ended=convert(varchar(30),o.ended)
  , DurationInMs=datediff(millisecond,x.ended,o.ended)
from #timer o
  outer apply (select top 1 ended from #timer i where i.ended < o.ended order by i.ended desc) as x

kehrt zurück:

+-------------------------------------+---------------------+---------------------+--------------+
|                what                 |       started       |        ended        | DurationInMs |
+-------------------------------------+---------------------+---------------------+--------------+
| Start                               | Feb 19 2017  7:23PM | Feb 19 2017  7:23PM | NULL         |
| Finished Loading Test Data          | Feb 19 2017  7:23PM | Feb 19 2017  7:23PM | 5440         |
| Finished Prep                       | Feb 19 2017  7:23PM | Feb 19 2017  7:23PM | 1513         |
| Finished "Pre-aggregated" Pivot     | Feb 19 2017  7:23PM | Feb 19 2017  7:23PM | 683          |
| Finished "Pre-aggregated" Cross Tab | Feb 19 2017  7:23PM | Feb 19 2017  7:23PM | 370          |
+-------------------------------------+---------------------+---------------------+--------------+

"Voraggregiert" mit CTE

Rextester: http://rextester.com/WCTJH5484

create table #timer (what varchar(64), ended datetime);
insert into #timer values ('Start',getdate());
go

SELECT TOP 300000 --<<Look!  Change this number for testing different size tables
        RowNum       = IDENTITY(INT,1,1),
        Company      = CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65)
                     + CHAR(ABS(CHECKSUM(NEWID()))%2+65),
        Amount       = CAST(ABS(CHECKSUM(NEWID()))%1000000/100.0 AS MONEY),
        Quantity     = ABS(CHECKSUM(NEWID()))%50000+1,
        Date         = CAST(RAND(CHECKSUM(NEWID()))*3653.0+36524.0 AS DATETIME),
        Year         = CAST(NULL AS SMALLINT),
        Quarter      = CAST(NULL AS TINYINT)
   INTO #SomeTable3
   FROM Master.sys.SysColumns t1
  CROSS JOIN
        Master.sys.SysColumns t2 

--===== Fill in the Year and Quarter columns from the Date column
 UPDATE #SomeTable3
    SET Year    = DATEPART(yy,Date),
        Quarter = DATEPART(qq,Date)

--===== A table is not properly formed unless a Primary Key has been assigned
     -- Takes about 1 second to execute.
  ALTER TABLE #SomeTable3
        ADD PRIMARY KEY CLUSTERED (RowNum)
CREATE NONCLUSTERED INDEX IX_#SomeTable3_Cover1 
    ON dbo.#SomeTable3 (Company, Year)
       INCLUDE (Amount, Quantity, Quarter) 

create statistics syearquarter on #sometable3(year,quarter) with fullscan, norecompute;
GO
insert into #timer values ('Finished Loading Test Data',getdate());
go
--=====--
--===== "Pre-aggregated" Pivot with CTE
;WITH
ctePreAgg AS
(SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity
   FROM #SomeTable3 
  GROUP BY Company,Year,Quarter
)
 SELECT amt.Company,
        amt.Year,
        COALESCE(amt.[1],0) AS Q1Amt,
        COALESCE(qty.[1],0) AS Q1Qty,
        COALESCE(amt.[2],0) AS Q2Amt,
        COALESCE(qty.[2],0) AS Q2Qty,
        COALESCE(amt.[3],0) AS Q3Amt,
        COALESCE(qty.[3],0) AS Q3Qty,
        COALESCE(amt.[4],0) AS Q4Amt,
        COALESCE(qty.[4],0) AS Q5Qty,
        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,
        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty
   into #prea_Pivot_wcte_prep
   FROM (SELECT Company, Year, Quarter, Amount FROM ctePreAgg) AS t1
        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt
  INNER JOIN 
        (SELECT Company, Year, Quarter, Quantity FROM ctePreAgg) AS t2
        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty
     ON qty.Company = amt.Company 
    AND qty.Year    = amt.Year         
  ORDER BY amt.Company, amt.Year
go
--insert into #timer values ('Finished "Pre-aggregated" Pivot with CTE',getdate());
go
--=====--
--===== "Pre-aggregated" Cross Tab with CTE
;WITH
ctePreAgg AS
(SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity
   FROM #SomeTable3 
  GROUP BY Company,Year,Quarter
)
 SELECT Company,
        Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,
        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,
        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,
        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,
        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,
        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,
        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,
        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,
        SUM(Amount)   AS TotalAmt,
        SUM(Quantity) AS TotalQty
   into #prea_CrossTab_wcte_prep
   FROM ctePreAgg
  GROUP BY Company, Year
  ORDER BY Company, Year
go
--insert into #timer values ('Finished "Pre-aggregated" Cross Tab with CTE',getdate());
go
--=====--
insert into #timer values ('Finished Prep',getdate());
go
--=====--
--===== "Pre-aggregated" Pivot with CTE
;WITH
ctePreAgg AS
(SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity
   FROM #SomeTable3 
  GROUP BY Company,Year,Quarter
)
 SELECT amt.Company,
        amt.Year,
        COALESCE(amt.[1],0) AS Q1Amt,
        COALESCE(qty.[1],0) AS Q1Qty,
        COALESCE(amt.[2],0) AS Q2Amt,
        COALESCE(qty.[2],0) AS Q2Qty,
        COALESCE(amt.[3],0) AS Q3Amt,
        COALESCE(qty.[3],0) AS Q3Qty,
        COALESCE(amt.[4],0) AS Q4Amt,
        COALESCE(qty.[4],0) AS Q5Qty,
        COALESCE(amt.[1],0)+COALESCE(amt.[2],0)+COALESCE(amt.[3],0)+COALESCE(amt.[4],0) AS TotalAmt,
        COALESCE(qty.[1],0)+COALESCE(qty.[2],0)+COALESCE(qty.[3],0)+COALESCE(qty.[4],0) AS TotalQty
   into #prea_Pivot_wcte
   FROM (SELECT Company, Year, Quarter, Amount FROM ctePreAgg) AS t1
        PIVOT (SUM(Amount) FOR Quarter IN ([1], [2], [3], [4])) AS amt
  INNER JOIN 
        (SELECT Company, Year, Quarter, Quantity FROM ctePreAgg) AS t2
        PIVOT (SUM(Quantity) FOR Quarter IN ([1], [2], [3], [4])) AS qty
     ON qty.Company = amt.Company 
    AND qty.Year    = amt.Year         
  ORDER BY amt.Company, amt.Year
go
insert into #timer values ('Finished "Pre-aggregated" Pivot with CTE',getdate());
go
--=====--
--===== "Pre-aggregated" Cross Tab with CTE
;WITH
ctePreAgg AS
(SELECT Company,Year,Quarter,SUM(Amount) AS Amount,SUM(Quantity) AS Quantity
   FROM #SomeTable3 
  GROUP BY Company,Year,Quarter
)
 SELECT Company,
        Year,
        SUM(CASE WHEN Quarter = 1 THEN Amount   ELSE 0 END) AS Q1Amt,
        SUM(CASE WHEN Quarter = 1 THEN Quantity ELSE 0 END) AS Q1Qty,
        SUM(CASE WHEN Quarter = 2 THEN Amount   ELSE 0 END) AS Q2Amt,
        SUM(CASE WHEN Quarter = 2 THEN Quantity ELSE 0 END) AS Q2Qty,
        SUM(CASE WHEN Quarter = 3 THEN Amount   ELSE 0 END) AS Q3Amt,
        SUM(CASE WHEN Quarter = 3 THEN Quantity ELSE 0 END) AS Q3Qty,
        SUM(CASE WHEN Quarter = 4 THEN Amount   ELSE 0 END) AS Q4Amt,
        SUM(CASE WHEN Quarter = 4 THEN Quantity ELSE 0 END) AS Q4Qty,
        SUM(Amount)   AS TotalAmt,
        SUM(Quantity) AS TotalQty
   into #prea_CrossTab_wcte
   FROM ctePreAgg
  GROUP BY Company, Year
  ORDER BY Company, Year
go
insert into #timer values ('Finished "Pre-aggregated" Cross Tab with CTE',getdate());
go
--=====--
select 
    o.what
  , started=isnull(convert(varchar(30),x.ended),o.ended)
  , ended=convert(varchar(30),o.ended)
  , DurationInMs=datediff(millisecond,x.ended,o.ended)
from #timer o
  outer apply (select top 1 ended from #timer i where i.ended < o.ended order by i.ended desc) as x

kehrt zurück:

+----------------------------------------------+---------------------+---------------------+--------------+
|                     what                     |       started       |        ended        | DurationInMs |
+----------------------------------------------+---------------------+---------------------+--------------+
| Start                                        | Feb 19 2017  7:25PM | Feb 19 2017  7:25PM | NULL         |
| Finished Loading Test Data                   | Feb 19 2017  7:25PM | Feb 19 2017  7:26PM | 5723         |
| Finished Prep                                | Feb 19 2017  7:26PM | Feb 19 2017  7:26PM | 950          |
| Finished "Pre-aggregated" Pivot with CTE     | Feb 19 2017  7:26PM | Feb 19 2017  7:26PM | 580          |
| Finished "Pre-aggregated" Cross Tab with CTE | Feb 19 2017  7:26PM | Feb 19 2017  7:26PM | 323          |
+----------------------------------------------+---------------------+---------------------+--------------+

Ich bin etwas skeptisch gegenüber dem Artikel bezüglich seines Zwecks. Dies scheint die Leute davon zu überzeugen, dass PIVOT auch nach dem Upgrade von 2000 auf 2005 nicht verwendet werden muss. Ich konnte nicht sofort ein Problem mit der Methodik feststellen, aber wie MartinSmith sagt, gibt es viele Dinge, die möglicherweise die Leistungstests verzerren können Ergebnisse. Ich dachte einmal, ich hätte die Zeit für einen stark genutzten SP einmal halbiert, fand dann aber heraus, dass beim Ändern des SP ein neuer Ausführungsplan generiert wurde und das Neukompilieren des Originals ebenfalls hilfreich wäre. In Wahrheit betrug meine Verbesserung nur 10%.
Matthew Sontum

6

Nur ein Hinweis, um eine falsche Annahme zu korrigieren:

Es gibt drei , nicht zwei Hauptmethoden, um einen Pivot zu schreiben. Der dritte verwendet einen Fahrertisch und mehrere Verknüpfungen (mit entweder LEFT JOINoder OUTER/CROSS APPLY):

Dies kann mehr oder weniger effizient sein, abhängig von verschiedenen Details (Tabellenverteilungen, Indizes usw.) und Anforderungen der spezifischen Pivot-Operation. Es gibt verschiedene Unterschiede zur SUM / GROUP BY-Methode:

  • Es kann vermieden werden, die gesamte Tabelle zu scannen, wenn geeignete Indizes vorhanden sind (angemessene Bedeutung: je nach WHEREKlausel, GROUP BYKlausel und zu aggregierenden Spalten unterschiedlich). Im konkreten Beispiel ist ein Index für (RateItemTypeID, RateID) INCLUDE (UnitPrice)if _WherClause_leer.

    • Dies kann bei mehreren Gelegenheiten von Vorteil sein:
      Wenn die Tabelle beispielsweise Hunderte verschiedener RateItemTypeIDWerte enthält, unsere Abfrage jedoch nur an wenigen interessiert ist. Wenn ich die gesamte Tabelle (oder sogar einen ganzen Index) scanne oder einen kleineren Teil eines engeren NCI suche, würde ich erwarten, dass der zweite effizienter ist.
    • Dies kann natürlich kontraproduktiv sein, da unterschiedliche Indizes für unterschiedliche WHEREPrädikate und aggregierte Spalten erforderlich sind.
  • Die GROUP BYund sogar die gesamte Fahrunterabfrage kann häufig durch eine andere Tabelle ersetzt werden (eine RateTabelle im spezifischen Beispiel).

  • Bei mehreren Pivot-Variationen können die GROUP BYin den Unterabfragen auch entfernt und die Unterabfragen in einfache Verknüpfungen konvertiert werden LEFT(wenn beispielsweise im speziellen Fall eine UNIQUEEinschränkung besteht (RateID, RateItemTypeID)). Dies zeigt, dass die SUMin der "SUM / GROUP BY" -Methode (in diesen Fällen) nur aufgrund der GROUP BYund Summierung eines Wertes (und mehrerer Nullen) vorhanden ist.

Die Abfrage:

SELECT 
    d.RateID,
    Sum1 = COALESCE(s1.Sum1, 0),
    Sum2 = COALESCE(s2.Sum2, 0),
    Sum3 = COALESCE(s3.Sum3, 0)  
FROM 
    ( SELECT RateID    -- 
      FROM rate_item 
      WHERE _WhereClause_
      GROUP BY RateID
    ) AS d             -- driving table with the DISTINCT RateID values
  OUTER APPLY
    ( SELECT Sum1 = SUM(r1.UnitPrice)
      FROM rate_item AS r1
      WHERE _WhereClause_
        AND r1.RateItemTypeID = 1 
        AND r1.RateID = d.RateID
    ) AS s1
  OUTER APPLY
    ( SELECT Sum2 = SUM(r2.UnitPrice)
      FROM rate_item AS r2
      WHERE _WhereClause_
        AND r2.RateItemTypeID = 2 
        AND r2.RateID = d.RateID
    ) AS s2
  OUTER APPLY
    ( SELECT Sum3 = SUM(r3.UnitPrice)
      FROM rate_item AS r3
      WHERE _WhereClause_
        AND r3.RateItemTypeID = 3 
        AND r3.RateID = d.RateID
    ) AS s3 ;

5

SQL Server transformiert die folgende Abfrage:

SELECT 
RateID, [1], [2], [3]
FROM PertinentRates
PIVOT (SUM(UnitPrice) FOR RateItemTypeID IN ([1], [2], [3])) PVT)

zu:

SELECT 
RateID
SUM(CASE WHEN RateItemTypeID = 1 THEN UnitPrice ELSE 0 END),
SUM(CASE WHEN RateItemTypeID = 2 THEN UnitPrice ELSE 0 END),
SUM(CASE WHEN RateItemTypeID = 3 THEN UnitPrice ELSE 0 END)
 FROM rate_item WHERE supplierid = 2882874 AND rateplanid = 1 AND rateitemtypeid IN (1, 2, 3)
          GROUP BY RateID

AFAIK entscheidet sich also für die Lesbarkeit

Unten ist eine kurze Demo:

CREATE TABLE #Sales (EmpId INT, Yr INT, Sales MONEY)
INSERT #Sales VALUES(1, 2005, 12000)
INSERT #Sales VALUES(1, 2006, 18000)
INSERT #Sales VALUES(1, 2007, 25000)
INSERT #Sales VALUES(2, 2005, 15000)
INSERT #Sales VALUES(2, 2006, 6000)
INSERT #Sales VALUES(3, 2006, 20000)
INSERT #Sales VALUES(3, 2007, 24000)

jetzt die abfragen

SELECT EmpId, [2005], [2006], [2007]
FROM (SELECT EmpId, Yr, Sales FROM #Sales) AS s
PIVOT (SUM(Sales) FOR Yr IN ([2005], [2006], [2007])) AS p    

select 
empid,
sum(Case when yr=2005 then sales end) '2005',
sum(Case when yr=2006 then sales end) '2006',
sum(Case when yr=2007 then sales end) '2007'
from
#sales
group by empid

Wenn nun beide Abfragen im Stapel ausgeführt werden, sind beide gleich teuer und die Pläne sind weitgehend gleich:

Geben Sie hier die Bildbeschreibung ein

das SHOWPLAN_TEXTist

Pivot

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1018]=(0) THEN NULL ELSE [Expr1019] END, [Expr1004]=CASE WHEN [Expr1020]=(0) THEN NULL ELSE [Expr1021] END, [Expr1005]=CASE WHEN [Expr1022]=(0) THEN NULL ELSE [Expr1023] END))
       |--Stream Aggregate(GROUP BY:([tempdb].[dbo].[#Sales].[EmpId]) DEFINE:([Expr1018]=COUNT_BIG(CASE WHEN [tempdb].[dbo].[#Sales].[Yr]=(2005) THEN [tempdb].[dbo].[#Sales].[Sales] ELSE NULL END), [Expr1019]=SUM(CASE WHEN [tempdb].[dbo].[#Sales].[Yr]=(2005) THEN [tempdb].[dbo].[#Sales].[Sales] ELSE NULL END), [Expr1020]=COUNT_BIG(CASE WHEN [tempdb].[dbo].[#Sales].[Yr]=(2006) THEN [tempdb].[dbo].[#Sales].[Sales] ELSE NULL END), [Expr1021]=SUM(CASE WHEN [tempdb].[dbo].[#Sales].[Yr]=(2006) THEN [tempdb].[dbo].[#Sales].[Sales] ELSE NULL END), [Expr1022]=COUNT_BIG(CASE WHEN [tempdb].[dbo].[#Sales].[Yr]=(2007) THEN [tempdb].[dbo].[#Sales].[Sales] ELSE NULL END), [Expr1023]=SUM(CASE WHEN [tempdb].[dbo].[#Sales].[Yr]=(2007) THEN [tempdb].[dbo].[#Sales].[Sales] ELSE NULL END)))
            |--Sort(ORDER BY:([tempdb].[dbo].[#Sales].[EmpId] ASC))
                 |--Table Scan(OBJECT:([tempdb].[dbo].[#Sales]))

FALL

  |--Compute Scalar(DEFINE:([Expr1003]=CASE WHEN [Expr1021]=(0) THEN NULL ELSE [Expr1022] END, [Expr1004]=CASE WHEN [Expr1023]=(0) THEN NULL ELSE [Expr1024] END, [Expr1005]=CASE WHEN [Expr1025]=(0) THEN NULL ELSE [Expr1026] END))
       |--Stream Aggregate(GROUP BY:([tempdb].[dbo].[#sales].[EmpId]) DEFINE:([Expr1021]=COUNT_BIG([Expr1006]), [Expr1022]=SUM([Expr1006]), [Expr1023]=COUNT_BIG([Expr1007]), [Expr1024]=SUM([Expr1007]), [Expr1025]=COUNT_BIG([Expr1008]), [Expr1026]=SUM([Expr1008])))
            |--Compute Scalar(DEFINE:([Expr1006]=CASE WHEN [tempdb].[dbo].[#sales].[Yr]=(2005) THEN [tempdb].[dbo].[#sales].[Sales] ELSE NULL END, [Expr1007]=CASE WHEN [tempdb].[dbo].[#sales].[Yr]=(2006) THEN [tempdb].[dbo].[#sales].[Sales] ELSE NULL END, [Expr1008]=CASE WHEN [tempdb].[dbo].[#sales].[Yr]=(2007) THEN [tempdb].[dbo].[#sales].[Sales] ELSE NULL END))
                 |--Sort(ORDER BY:([tempdb].[dbo].[#sales].[EmpId] ASC))
                      |--Table Scan(OBJECT:([tempdb].[dbo].[#sales]))

@ TheGameiswar gibt es einen Unterschied zwischen den ersten 2 Abfragen in Ihrer Antwort ( supplierid = 2882874 AND rateplanid = 1)
ypercubeᵀᴹ
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.