Cái nào nhanh hơn, SUM (CASE) hay CTE PIVOT?


8

Có hai loại cách để thực hiện a PIVOT. Trước SQL Server 2005, khi PIVOTđược giới thiệu, hầu hết mọi người đã làm điều này:

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

Sau này, khi năm 2005 giới thiệu PIVOTnó đã trở thành thế này:

   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)

Trên toàn SQL Server 2005, 2008 R2, 2012 và 2014 (các phiên bản SQL Server tôi đã làm việc với triển khai đó PIVOT), theo kinh nghiệm của tôi, nó luôn luôn nhanh hơn SUM(CASE)hoặc trong một vài trường hợp nhanh như nhau. Có ví dụ nơi nào PIVOTchậm hơn?

Tôi không thể đưa ra DDL vì đây là một ví dụ từ công việc của tôi. Nhưng cái bàn khá đơn giản. Trong PIVOTví dụ này, nó được vẽ từ CTE trong khi đó SUM(CASE)là vẽ trực tiếp từ bảng. Nhưng SUM(CASE)thực hiện cùng một bản vẽ từ CTE.

Trong ví dụ công việc của tôi, sự PIVOTtrở lại sau 10 giây trong khi sự SUM(CASE)trở lại sau 14. Rõ ràng nó phải làm một cái gì đó khác biệt dưới vỏ bọc. Các kế hoạch là như nhau, 50% tổng số mỗi. PIVOTchuyển đổi thành SUM(CASE)trong bộ phân tích truy vấn. Chưa SUM(CASE)bao giờ quay lại sau chưa đầy 13 giây và PIVOTkhông bao giờ quay lại sau hơn 11 giây.

Tôi đã thử chạy chúng qua lại, không quan trọng thứ tự chúng được chạy. Nếu tôi chạy cả hai từ bộ đệm lạnh thì cả hai đều mất nhiều thời gian hơn, nhưng PIVOTvẫn nhanh hơn, 12 so với 17 giây. Không thể sao chép trên máy chủ thứ hai, nhưng máy chủ đó tốt hơn đáng kể; mỗi giây có 5 giây với các biến thể nhỏ. PIVOTtốt hơn một chút, nhưng phần trăm khôn ngoan, nó không có cùng lợi thế với máy chủ đầu tiên.

Số liệu thống kê IO, giống như kế hoạch truy vấn, giống hệt nhau giữa hai. Điều đó thật kỳ lạ, tôi đã dự kiến ​​sẽ thấy các số liệu thống kê IO khác nhau, mặc dù tôi chưa bao giờ nhìn vào chúng cho ví dụ cụ thể này.

Câu trả lời:


22

Có ví dụ nơi nào PIVOTchậm hơn?

Điều này là không thể trong các trường hợp đơn giản . Như Itzik Ben-Gan lưu ý trong bài viết SQL Server Pro của mình, Xoay vòng dữ liệu khi xem kế hoạch cho một PIVOTtruy vấn (nhấn mạnh thêm):

Hình 3 cho thấy kế hoạch cho PIVOTtruy vấn. Như bạn có thể thấy, kế hoạch này rất giống với giải pháp tiêu chuẩn, rất nhiều đến nỗi nếu bạn nhìn vào các thuộc tính của toán tử Tổng hợp, trong Các giá trị được xác định, bạn sẽ thấy rằng SQL Server xây dựng các CASEbiểu thức đằng sau hậu trường:

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

Với suy nghĩ này, bạn không nên mong đợi giải pháp dựa trên PIVOTtoán tử sẽ hoạt động tốt hơn giải pháp chuẩn. Lợi ích chính trong PIVOTtoán tử tại thời điểm này là nó ít dài dòng hơn.

Đối với các yêu cầu xoay vòng nâng cao hơn mà PIVOTcú pháp (không chuẩn) không hỗ trợ trực tiếp, cần có cách giải quyết. Những điều này có thể hoặc không thể dẫn đến hiệu suất kém hơn so với CASE, tùy thuộc vào các yếu tố khác nhau bao gồm mức độ kỹ năng của người thực hiện.

Ví dụ về các trường hợp có vấn đề này được đề cập trong bài viết của Itzik và cũng được giải thích rõ trong bài viết Nói chuyện đơn giản của Robert Sheldon, Câu hỏi về Dữ liệu Xoay vòng trong Máy chủ SQL mà bạn quá ngại hỏi .

Kinh nghiệm của tôi là điều đó PIVOTAgg(CASE...tạo ra các kế hoạch cực kỳ giống nhau với các đặc tính hiệu suất cực kỳ gần gũi khi cả hai đều được viết một cách tối ưu. Lời khuyên thông thường của tôi là viết các truy vấn bằng bất kỳ cú pháp nào cảm thấy tự nhiên nhất đối với bạn và chỉ thử viết lại nếu hiệu suất không được chấp nhận.

Nội bộ

Bộ xử lý truy vấn SQL Server không có một built-in Pivot logic điều hành ( LogOp_Pivot), vì vậy nó có thể không hoàn toàn đúng khi nói rằng SQL Server ghi đè trục để uẩn và các biểu thức trường hợp, ít nhất là nếu chúng ta đang nói về phân tích cú pháp và các hoạt động biên soạn mà mất đặt trước khi tối ưu hóa dựa trên chi phí (kế hoạch tầm thường không có sẵn cho các truy vấn trục).

Mặt khác, đúng là cách duy nhất trình tối ưu hóa có thể thực hiện một cây truy vấn có chứa LogOp_Pivotlà thông qua quy tắc thăm dò ExpandPivot. Quy tắc này mở rộng LogOp_Pivotthành một tổng hợp nhóm ( LogOp_GbAgg) bình thường với các biểu thức vô hướng liên quan. Khi quy tắc này bị vô hiệu hóa, các truy vấn trục không thể biên dịch.

Trong thực tế sau đó, chúng ta có thể nói rằng pivots luôn luôn (cuối cùng) được 'viết lại' dưới dạng tổng hợp và biểu thức vô hướng trước khi có thể tạo ra một kế hoạch thực thi.

Dù sao, kết quả của việc viết lại LogOp_GbAggđược chuyển đổi thành các toán tử vật lý cần thiết cho một kế hoạch thực thi theo quy tắc thực hiện tổng hợp theo nhóm thông thường GbAggToHS(băm) hoặc GbAggToStrm(luồng).

Như một lưu ý phụ, lý do 'pivots thủ công' (tổng hợp trên biểu thức trường hợp) có thêm một tính toán vô hướng bên dưới tổng hợp là các biểu thức trường hợp được đẩy về phía cấp độ của cây truy vấn trong quá trình Chuẩn hóa dự án (giai đoạn đầu của quá trình biên dịch, trước khi tối ưu hóa dựa trên chi phí).

Các truy vấn sử dụng PIVOTcú pháp không có điều này vì các biểu thức không được tạo cho đến khi ExpandPivotchạy trong quá trình tối ưu hóa dựa trên chi phí. Tại thời điểm (sớm hơn) Chuẩn hóa dự án chạy, cây truy vấn vẫn có LogOp_Pivotcác phần tử, do đó không có dự đoán nào để đẩy xuống và các biểu thức trường hợp thường kết thúc bên trong hàm băm hoặc luồng.

Thông thường không có lợi thế trong việc tránh vô hướng tính toán, vì từ SQL Server 2005 trở đi, việc đánh giá biểu thức thường được hoãn lại cho đến khi kết quả được yêu cầu bởi một toán tử sau. Trong trường hợp này, việc đánh giá các biểu thức trường hợp được hoãn lại cho đến khi tổng hợp (hàm băm hoặc luồng) yêu cầu.


12

Lặp lại các thử nghiệm từ Cross Tab và Pivots, Phần 1 - Chuyển đổi hàng thành cột - Tác giả Jeff Moden, 2010/08/06 (xuất bản lần đầu: 2008/08/19) trên rextester

Thật không may, tôi không thể truy cập số liệu thống kê cho IO, Thời gian hoặc kế hoạch thực hiện trên rextester, nhưng nó có lợi ích duy nhất là một môi trường thử nghiệm phổ biến mà bất kỳ ai ở đây cũng có thể sửa đổi và kiểm tra. Tôi nhận ra điều này vẫn để lại điều gì đó mong muốn liên quan đến việc đào sâu và điều tra chính xác những gì đang diễn ra, nhưng tôi sẽ nói rằng việc có thể chia sẻ một môi trường thử nghiệm là một khía cạnh quan trọng cho cuộc thảo luận này.


Đơn giản

rextester: http://rextester.com/BAZMGJ69528

Cái này đã được thêm cho @MartinSmith và trong khi các truy vấn được lấy từ cùng một bài viết, nó không có trong các thử nghiệm ban đầu như thế này:

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

trả về:

+----------------------------+---------------------+---------------------+--------------+
|            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          |
+----------------------------+---------------------+---------------------+--------------+

Phần còn lại tất cả các giới hạn kiểm tra trong pivotcú pháp trong đó một truy vấn tab chéo có thể thực hiện được yêu cầu nhiều pivots.

Bình thường

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

trả về:

+----------------------------+---------------------+---------------------+--------------+
|            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          |
+----------------------------+---------------------+---------------------+--------------+

"Tổng hợp trước"

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

trả về:

+-------------------------------------+---------------------+---------------------+--------------+
|                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          |
+-------------------------------------+---------------------+---------------------+--------------+

"Tổng hợp trước" với 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

trả về:

+----------------------------------------------+---------------------+---------------------+--------------+
|                     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          |
+----------------------------------------------+---------------------+---------------------+--------------+

Tôi hơi nghi ngờ về bài viết liên quan đến mục đích của nó. Có vẻ như để thuyết phục mọi người, không cần thiết phải sử dụng PIVOT ngay cả sau khi nâng cấp từ năm 2000 đến năm 2005. Tôi không thể ngay lập tức tìm thấy một vấn đề với phương pháp, nhưng, như MartinSmith nói, có nhiều điều có thể có khả năng kiểm tra hiệu suất sai lệch các kết quả. Tôi đã từng nghĩ rằng tôi đã cắt giảm thời gian cho một SP được sử dụng nhiều một nửa, nhưng sau đó thấy rằng đó là vì khi thay đổi SP, nó đã tạo ra một kế hoạch thực hiện mới, và biên dịch lại bản gốc cũng sẽ giúp nó. Trong thực tế, sự cải thiện của tôi chỉ là 10%.
Matthew Sontum

6

Chỉ cần một lưu ý để sửa một giả định sai:

ba , không phải hai, cách chính để viết một trục. Thứ ba là sử dụng một bảng lái xe và một số tham gia (với một trong hai LEFT JOINhoặc OUTER/CROSS APPLY):

Điều này có thể hiệu quả hơn hoặc ít hơn, tùy thuộc vào một số chi tiết (phân phối bảng, chỉ mục, v.v.) và các yêu cầu của hoạt động trục cụ thể. Nó có một số khác biệt với phương thức SUM / GROUP BY:

  • nó có thể tránh quét toàn bộ bảng nếu có các chỉ mục thích hợp (ý nghĩa phù hợp: khác nhau tùy thuộc vào WHEREmệnh đề, GROUP BYmệnh đề và các cột được tổng hợp). Trong ví dụ cụ thể, một chỉ mục trên (RateItemTypeID, RateID) INCLUDE (UnitPrice)nếu _WherClause_trống.

    • Điều này có thể có ích trong nhiều trường hợp:
      Ví dụ : nếu bảng có hàng trăm RateItemTypeIDgiá trị khác nhau nhưng truy vấn của chúng tôi chỉ quan tâm đến một số ít. Quét toàn bộ bảng (hoặc thậm chí toàn bộ chỉ mục) so với tìm kiếm một phần nhỏ hơn của NCI hẹp hơn, tôi hy vọng lần thứ 2 sẽ hiệu quả hơn.
    • Tất nhiên nó có thể phản tác dụng vì nó yêu cầu các chỉ mục khác nhau cho các WHEREvị từ và cột tổng hợp khác nhau .
  • các GROUP BYvà thậm chí toàn bộ subquery lái xe thường có thể được thay thế bằng một bảng (một Ratebảng trong ví dụ cụ thể).

  • trên một số biến thể trục, GROUP BYtrong các truy vấn con cũng có thể được loại bỏ và các truy vấn con được chuyển đổi thành các phép LEFTnối đơn giản (ví dụ: nếu có một UNIQUEràng buộc (RateID, RateItemTypeID)trong trường hợp cụ thể). Điều này cho thấy rằng SUMtrong phương thức "SUM / GROUP BY" là (trong những trường hợp này) chỉ có bởi vì GROUP BYvà tổng một giá trị (và một số Null).

Truy vấn:

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 biến đổi bên dưới truy vấn:

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

đến:

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

Vì vậy, chọn cái này qua cái khác, AFAIK đạt được khả năng dễ đọc

Dưới đây là bản demo ngắn:

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)

bây giờ các truy vấn

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

bây giờ khi cả hai truy vấn được thực hiện theo lô, cả hai đều chiếm cùng một chi phí và các gói giống nhau:

nhập mô tả hình ảnh ở đây

những SHOWPLAN_TEXT

Trục

  |--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]))

TRƯỜNG HỢP

  |--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 có một sự khác biệt giữa 2 truy vấn đầu tiên trong câu trả lời của bạn (supplierid = 2882874 AND rateplanid = 1 )
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.