如何在 SQL 中创建矩阵/如何使用大量行进行透视
Posted
技术标签:
【中文标题】如何在 SQL 中创建矩阵/如何使用大量行进行透视【英文标题】:How to create a Matrix in SQL / How to Pivot with a lot of rows 【发布时间】:2019-09-27 18:43:16 【问题描述】:我有一个简单的发票表,其中包含每件售出的商品及其售出日期。
我想创建一个矩阵,左侧是从上到下的商品编号列表,顶部是从 1 到 52 周的列表,中间是该商品的销售量那一周。
这是我目前所拥有的。它只是一个星期,项目编号,以及它在那一周卖出了多少作为一个简单的列表。
这里是查询:
SELECT
*
FROM (
SELECT
Week
,Item_Number
,Color_Code
,Count(1) Touches
FROM (
SELECT
DATEPART (year, I.Date_Invoiced) Year
,DATEPART (month, I.Date_Invoiced) Month
,DATEPART (week, I.Date_Invoiced) Week
,DATEPART (day, I.Date_Invoiced) Day
,I.Invoice_Number
,I.Customer_Number
,I.Warehouse_Code
,S.Pack_Type
,S.Quantity_Per_Carton
,S.Inner_Pack_Quantity
,ID.Item_Number
,ID.Color_Code
,ID.Quantity
,case when s.Pack_Type='carton' then id.Quantity/s.Quantity_Per_Carton when s.Pack_Type='Inner Poly' then id.Quantity/s.Inner_Pack_Quantity end qty
,ID.Line_Number
FROM Invoices I
LEFT JOIN Invoices_Detail ID on I.Company_Code = ID.Company_Code and I.Division_Code = ID.Division_Code and I.Invoice_Number = ID.Invoice_Number
LEFT JOIN Style S on I.Company_Code = S.Company_Code and I.Division_Code = S.Division_Code and ID.Item_Number = S.Item_Number and ID.Color_Code = S.Color_Code
WHERE 1=1
AND (I.Company_Code = @LocalCompanyCode OR @LocalCompanyCode IS NULL)
AND (I.Division_Code = @LocalDivisionCode OR @LocalDivisionCode IS NULL)
AND (I.Warehouse_Code = @LocalWarehouse OR @LocalWarehouse IS NULL)
AND (S.Pack_Type = @LocalPackType OR @LocalPackType IS NULL)
AND (I.Customer_Number = @LocalCustomerNumber OR @LocalCustomerNumber IS NULL)
AND (I.Date_Invoiced Between @LocalFromDate And @LocalToDate)
AND Inner_Pack_Quantity > 1
) T
GROUP BY Week, Item_Number, Color_Code
) TT
ORDER BY Week, Item_Number
还有一些示例数据:
+------+-----------------+------------+---------+
| Week | Item_Number | Color_Code | Touches |
+------+-----------------+------------+---------+
| 1 | 11073900LRGMO | 02000 | 7 |
| 1 | 11073900MEDMO | 02000 | 9 |
| 2 | 1114900011BMO | 38301 | 62 |
| 2 | 1114910012BMO | 21701 | 147 |
| 2 | 1114910012BMO | 38301 | 147 |
| 2 | 1114910012BMO | 46260 | 147 |
| 3 | 13MK430R03R | 00101 | 2 |
| 3 | 13MK430R03R | 10001 | 2 |
| 3 | 13MK430R03R | 65004 | 8 |
| 3 | 13MK430R03S | 00101 | 2 |
| 3 | 13MK430R03S | 10001 | 2 |
+------+-----------------+------------+---------+
我正在查看一些关于如何进行 Pivot 的指南,但他们似乎都说您需要指定每个列名,这在我的情况下会非常困难,因为我有数百个项目而且我不知道它们都是什么会提前。
【问题讨论】:
【参考方案1】:您可以尝试使用动态查询来完成这项工作,这样您就不必提前知道报告中包含多少周。我使用您的示例数据使用 SQL Server 2014 演示了这一点,我还添加了一些额外的记录以实现效果。如果您不知道数据透视表是如何工作的,那么这将很难理解,但是使用这个示例,您很快就会得到它。第一个动态语句通过游标为您的数据透视构建列列表,然后将它们与数据透视所需的其余查询连接在一起。最后整个事情都被执行了。
-- I created a temp table out of your sample data to show how the dynamic query will work,
-- also added some extra data to demonstrate that you don't need to know the number of weeks in advance.
-- You should replace the the temp table with your query as a table or store the results in a global temp.
-- Notice that the temp table is global this is because the dynaimic queries will run on thier own
-- session and not have access to the temp if you declare it as #dynPivot instead use ##dynPivot
IF OBJECT_ID('tempdb..##dynPivot') IS NOT NULL
BEGIN
DROP TABLE ##dynPivot
END
GO
-- Create temp
CREATE TABLE ##dynPivot(
[Week] INT,
Item_Number VARCHAR(50),
Color_Code VARCHAR(50),
Touches INT
)
-- populate temp with sample data
INSERT INTO ##dynPivot([Week], Item_Number, Color_Code, Touches)
VALUES (1, '11073900LRGMO','02000',7), (1, '11073900MEDMO', '02000',9), (2,
'1114900011BMO', '38301',62), (2, '1114910012BMO', '21701',147), (2, '1114910012BMO', '38301',147), (2, '1114910012BMO', '46260',147),
(3, '13MK430R03R', '00101',2), (3, '13MK430R03R', '10001',2), (3, '13MK430R03R', '65004',8),
(3, '13MK430R03S', '00101',2), (3, '13MK430R03S', '10001',2), (4, '13MK430R03S', '10001',2), (5, '13MK430R03S', '10001',6),
(9, '13MK430R03S', '10001',2),(10, '13MK430R03S', '10001',1),(6, '13MK430R03S', '10001',1),(3, '13MK430R03S', '10001',9),
(4, '13MK430R03R', '00101',2), (8, '13MK430R03R', '10001',126), (7, '13MK430R03R', '65004',8),
(10, '1114910012ACB', '21701',147), (20, '1114910012XYZ', '38301',17), (12, '1114910012BMO', '46260',14)
-- Build the two dynaimc queries
DECLARE @sql VARCHAR(MAX) = ''
SET @sql = 'DECLARE @innerSql VARCHAR(MAX) = ''''
DECLARE @week VARCHAR(50)
DECLARE @count INT
DECLARE @selectList VARCHAR(500) = ''''
DECLARE @counter INT
SET @counter = (SELECT COUNT(DISTINCT[week]) FROM ##dynPivot)
DECLARE C CURSOR FOR SELECT [week] FROM ##dynPivot GROUP BY [week] ORDER BY [week]
OPEN c
FETCH NEXT FROM C INTO @week
WHILE @@FETCH_STATUS = 0
BEGIN
IF @counter > 1
BEGIN
SET @selectList = @selectList + ''['' + @week + ''], ''
END
ELSE
BEGIN
SET @selectList = @selectList + ''['' + @week + '']''
END
SET @counter = @counter - 1
FETCH NEXT FROM C INTO @week
END
CLOSE C
DEALLOCATE C
SET @innerSql = @innerSql + ''SELECT Item_Number, '' + @selectList + '' FROM (SELECT [Week], Item_Number, Touches FROM ##dynPivot) AS DataSorce'' +
'' PIVOT (SUM(Touches) FOR [week] IN ('' + @selectList + '')) AS PT''
EXEC(@innerSql)'
--Execute them
EXEC(@sql)
-- You should get the following results
-- Item_Number 1 2 3 4 5 6 7 8 9 10 12 20
-- 11073900LRGMO 7 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
-- 11073900MEDMO 9 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
-- 1114900011BMO NULL 62 NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
-- 1114910012ACB NULL NULL NULL NULL NULL NULL NULL NULL NULL 147 NULL NULL
-- 1114910012BMO NULL 441 NULL NULL NULL NULL NULL NULL NULL NULL 14 NULL
-- 1114910012XYZ NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL 17
-- 13MK430R03R NULL NULL 12 2 NULL NULL 8 126 NULL NULL NULL NULL
-- 13MK430R03S NULL NULL 13 2 6 1 NULL NULL 2 1 NULL NULL
【讨论】:
以上是关于如何在 SQL 中创建矩阵/如何使用大量行进行透视的主要内容,如果未能解决你的问题,请参考以下文章