Hiểu hàm PIVOT trong T-SQL


82

Tôi rất mới đối với SQL.

Tôi có một bảng như thế này:

ID | TeamID | UserID | ElementID | PhaseID | Effort
-----------------------------------------------------
1  |   1    |  1      |   3       |  5     |   6.74
2  |   1    |  1      |   3       |  6     |   8.25
3  |   1    |  1      |   4       |  1     |   2.23
4  |   1    |  1      |   4       |  5     |   6.8
5  |   1    |  1      |   4       |  6     |   1.5

Và tôi được yêu cầu lấy dữ liệu như thế này

ElementID | PhaseID1 | PhaseID5 | PhaseID6
--------------------------------------------
    3     |   NULL   |   6.74   |   8.25
    4     |   2.23   |   6.8    |   1.5

Tôi hiểu rằng tôi cần sử dụng chức năng PIVOT. Nhưng không thể hiểu nó một cách rõ ràng. Sẽ rất hữu ích nếu ai đó có thể giải thích nó trong trường hợp trên. (Hoặc bất kỳ lựa chọn thay thế nào nếu có)

Câu trả lời:


108

A PIVOTđược sử dụng để xoay dữ liệu từ một cột thành nhiều cột.

Đối với ví dụ của bạn, đây là STATIC Pivot nghĩa là bạn viết mã cố định các cột mà bạn muốn xoay:

create table temp
(
  id int,
  teamid int,
  userid int,
  elementid int,
  phaseid int,
  effort decimal(10, 5)
)

insert into temp values (1,1,1,3,5,6.74)
insert into temp values (2,1,1,3,6,8.25)
insert into temp values (3,1,1,4,1,2.23)
insert into temp values (4,1,1,4,5,6.8)
insert into temp values (5,1,1,4,6,1.5)

select elementid
  , [1] as phaseid1
  , [5] as phaseid5
  , [6] as phaseid6
from
(
  select elementid, phaseid, effort
  from temp
) x
pivot
(
  max(effort)
  for phaseid in([1], [5], [6])
)p

Đây là bản trình diễn SQL với phiên bản đang hoạt động.

Điều này cũng có thể được thực hiện thông qua PIVOT động, nơi bạn tạo danh sách cột động và thực hiện PIVOT.

DECLARE @cols AS NVARCHAR(MAX),
    @query  AS NVARCHAR(MAX);

select @cols = STUFF((SELECT distinct ',' + QUOTENAME(c.phaseid) 
            FROM temp c
            FOR XML PATH(''), TYPE
            ).value('.', 'NVARCHAR(MAX)') 
        ,1,1,'')

set @query = 'SELECT elementid, ' + @cols + ' from 
            (
                select elementid, phaseid, effort
                from temp
           ) x
            pivot 
            (
                 max(effort)
                for phaseid in (' + @cols + ')
            ) p '


execute(@query)

Kết quả cho cả hai:

ELEMENTID   PHASEID1    PHASEID5    PHASEID6
3           Null        6.74        8.25
4           2.23        6.8         1.5

1
Cảm ơn đã hiểu. Chỉ có điều tôi cần phải viết mã PhaseIDtrước QUOTENAME. đúng?
Web-E

1
trong QUOTENAME, bạn phải xác định cột nào bạn cần để lấy các giá trị từ đó. đó có phải là điều mà bạn đang hỏi?
Taryn

Để thực hiện các công việc giải pháp STUFF với những cái tên lạ cột (khoảng trắng, dấu ngoặc đơn, vv) tôi phải làm SELECT distinct '],[', và cũng có thể ở phần cuối của báo cáo kết quả1, 2, '') + ']'
Nat

@ Web-E, rất tiếc là có. Một giải pháp khác là bạn có thể viết chuỗi truy vấn trong ứng dụng của mình hoặc chơi với SQL động trong một quy trình được lưu trữ.
MarcoM

7

Đây là ví dụ xoay rất cơ bản, vui lòng xem qua điều đó.

SQL SERVER - Ví dụ về bảng PIVOT và UNPIVOT

Ví dụ từ liên kết trên cho bảng sản phẩm:

SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
 PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT

kết xuất:

 PRODUCT FRED  KATE
 --------------------
 BEER     24    12
 MILK      3     1
 SODA   NULL     6
 VEG    NULL     5

Các ví dụ tương tự có thể được tìm thấy trong bài đăng blog Bảng tổng hợp trong SQL Server. Một mẫu đơn giản


cũng lưu ý, nếu bạn kéo thêm cột số từ bảng nguồn, trục xoay sẽ chia kết quả thành nhiều hàng. Ví dụ SELECT CUST, VEG, SODA FROM (SELECT rand() as x, CUST, PRODUCT, QTY FROM Product) up PIVOT ( SUM(x) FOR PRODUCT IN (VEG, SODA) ) AS pvt ORDER BY CUST GO Để cho tiện làm việc, bạn phải gỡ bỏ qtycột từ nguồn
Raheel Hasan

4

Tôi có một cái gì đó để thêm ở đây mà không ai đề cập đến.

Các pivotchức năng hoạt động tuyệt vời khi nguồn có 3 cột: Một cho aggregate, một để lây lan như cột với for, và một là một trục cho rowphân phối. Trong ví dụ sản phẩm đó là QTY, CUST, PRODUCT.

Tuy nhiên, nếu bạn có nhiều cột hơn trong nguồn, nó sẽ chia kết quả thành nhiều hàng thay vì một hàng trên mỗi tổng hợp dựa trên các giá trị duy nhất trên mỗi cột bổ sung (như Group Bysẽ làm trong một truy vấn đơn giản).

Xem ví dụ này, tôi đã thêm cột dấu thời gian vào bảng nguồn:

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

Bây giờ hãy xem tác động của nó:

SELECT CUST, MILK

FROM Product
-- FROM (SELECT CUST, Product, QTY FROM PRODUCT) p
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

ORDER BY CUST

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


Để khắc phục điều này, bạn có thể lấy một truy vấn con làm nguồn như mọi người đã làm ở trên - chỉ với 3 cột (điều này không phải lúc nào cũng hoạt động cho kịch bản của bạn, hãy tưởng tượng nếu bạn cần đặt một wheređiều kiện cho dấu thời gian).

Giải pháp thứ hai là sử dụng a group byvà thực hiện lại tổng các giá trị cột đã xoay.

SELECT 
CUST, 
sum(MILK) t_MILK

FROM Product
PIVOT (
    SUM(QTY) FOR PRODUCT IN (MILK)
) AS pvt

GROUP BY CUST
ORDER BY CUST

GO

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


4

Một pivot được sử dụng để chuyển đổi một trong các cột trong tập dữ liệu của bạn từ hàng thành cột (cột này thường được gọi là cột dàn trải ). Trong ví dụ bạn đã đưa ra, điều này có nghĩa là chuyển đổi các PhaseIDhàng thành một tập hợp các cột, trong đó có một cột cho mỗi giá trị riêng biệt PhaseIDcó thể chứa - 1, 5 và 6 trong trường hợp này.

Các giá trị xoay vòng này được nhóm lại qua ElementIDcột trong ví dụ mà bạn đã cung cấp.

Thông thường, sau đó, bạn cũng cần cung cấp một số dạng tổng hợp cung cấp cho bạn các giá trị được tham chiếu bởi giao điểm của giá trị trải rộng ( PhaseID) và giá trị nhóm ( ElementID). Mặc dù trong ví dụ đưa ra, tập hợp sẽ được sử dụng không rõ ràng, nhưng liên quan đến Effortcột.

Sau khi hoàn tất việc xoay vòng này, các cột nhómcột trải rộng được sử dụng để tìm giá trị tổng hợp . Hoặc trong trường hợp của bạn, ElementIDPhaseIDXtra cứu Effort.

Sử dụng thuật ngữ nhóm, dàn trải, tổng hợp, bạn thường sẽ thấy cú pháp ví dụ cho một trục như:

WITH PivotData AS
(
    SELECT <grouping column>
        , <spreading column>
        , <aggregation column>
    FROM <source table>
)
SELECT <grouping column>, <distinct spreading values>
FROM PivotData
    PIVOT (<aggregation function>(<aggregation column>)
        FOR <spreading column> IN <distinct spreading values>));

Điều này cung cấp giải thích bằng đồ họa về cách các cột nhóm, dàn trải và tổng hợp chuyển đổi từ nguồn sang bảng xoay nếu điều đó giúp ích thêm.


3

Để đặt lỗi Tương thích

sử dụng cái này trước khi sử dụng hàm pivot

ALTER DATABASE [dbname] SET COMPATIBILITY_LEVEL = 100  

3
    SELECT <non-pivoted column>,
    [first pivoted column] AS <column name>,
    [second pivoted column] AS <column name>,
    ...
    [last pivoted column] AS <column name>
FROM
    (<SELECT query that produces the data>)
    AS <alias for the source query>
PIVOT
(
    <aggregation function>(<column being aggregated>)
FOR
[<column that contains the values that will become column headers>]
    IN ( [first pivoted column], [second pivoted column],
    ... [last pivoted column])
) AS <alias for the pivot table>
<optional ORDER BY clause>;

USE AdventureWorks2008R2 ;
GO
SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost 
FROM Production.Product
GROUP BY DaysToManufacture;

    DaysToManufacture          AverageCost
0                          5.0885
1                          223.88
2                          359.1082
4                          949.4105

    -- Pivot table with one row and five columns
SELECT 'AverageCost' AS Cost_Sorted_By_Production_Days, 
[0], [1], [2], [3], [4]
FROM
(SELECT DaysToManufacture, StandardCost 
    FROM Production.Product) AS SourceTable
PIVOT
(
AVG(StandardCost)
FOR DaysToManufacture IN ([0], [1], [2], [3], [4])
) AS PivotTable;




Here is the result set.
Cost_Sorted_By_Production_Days    0         1         2           3       4       
AverageCost                       5.0885    223.88    359.1082    NULL    949.4105

1
tại sao <SELECT query that produces the data>không chỉ là cái bàn?
Raheel Hasan
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.