Как создать Матрицу в SQL / Как Pivot с большим количеством строк - PullRequest
0 голосов
/ 27 сентября 2019

У меня есть простая таблица счетов-фактур, в которой продается каждый предмет и дата его продажи.

Я хочу создать матрицу, где слева - список номеров предметов, идущий сверху вниз, а сверху - список недель от 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 Ответ

0 голосов
/ 28 сентября 2019

Одна вещь, которую вы можете попробовать, - это использовать динамические запросы для выполнения этой задачи, тогда вам не нужно будет заранее знать, сколько недель включено в отчет.Я использовал ваши примеры данных, чтобы продемонстрировать это с помощью 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
...