/ / PIVOT z wieloma kolumnami - sql-server, sql-server-2008, pivot, unpivot

PIVOT z wieloma kolumnami - sql-server, sql-server-2008, pivot, unpivot

Próbuję przestawić się na dwóch kolumnach w SQL Server 2008 w tabeli faktur. Mam więc następujące dane:

+--------------+--------+---------+------+
| Invoice Date | Item # | Dollars | Lbs. |
+--------------+--------+---------+------+
| 1/1/14       | A      |       1 |    1 |
| 1/2/14       | B      |       2 |    2 |
| 1/3/14       | A      |       3 |    3 |
| 1/4/14       | B      |       4 |    4 |
| 2/1/14       | A      |       5 |    5 |
| 2/1/14       | B      |       6 |    6 |
+--------------+--------+---------+------+

Chciałbym wyświetlić go jako

+--------+--------------+-----------------+--------------+-----------------+
| Item # | 1/31/14 Lbs. | 1/31/14 Dollars | 2/28/14 Lbs. | 2/28/14 Dollars |
+--------+--------------+-----------------+--------------+-----------------+
| A      |            4 |               4 |            5 |               5 |
| B      |            6 |               6 |            6 |               6 |
+--------+--------------+-----------------+--------------+-----------------+

Pamiętaj, że nazwa kolumny to ostatni dzień tego miesiąca i dolary lub funty. Mogę to zrobić tylko jedną kolumnę (funty lub dolary), ale nie mogę tego zrobić na obu.

Oto mój przykładowy kod tylko funtów:

DECLARE
@v_Columns VARCHAR(MAX),
@v_Query VARCHAR(MAX)

--pivot and delimit values

SELECT @v_Columns = COALESCE(@v_Columns,"[") + convert(varchar(8), InvoiceDate, 1) + " Lbs." + "],["
FROM
( SELECT DISTINCT dbo.ufn_GetLastDayOfMonth(InvoiceDate) As InvoiceDate
FROM Invoice
WHERE InvoiceDate BETWEEN @BEGIN_DATE AND @END_DATE
ORDER BY InvoiceDate

--delete last two chars of string (the ending ",[")

SET @v_Columns = SUBSTRING(@v_Columns, 1, LEN(@v_Columns)-2)
PRINT @v_Columns

--construct sql statement

SET @v_Query =

"WITH AllOrders (LastInvoiceDate, Item,  Pounds) AS
(
SELECT
CONVERT(varchar(8), dbo.ufn_GetLastDayOfMonth(Invoice.InvoiceDate), 1) + """ + " Lbs." + """ As LastInvoiceDate,
Item,
Pounds
FROM INVOICE
WHERE InvoiceDate BETWEEN @BEGIN_DATE AND  @END_DATE
)
SELECT *
FROM AllOrders
PIVOT
(
SUM(QuantityShipped)
FOR LastInvoiceDate  IN (" + @v_Columns + ")
) AS pivotview"

Dziękuję wszystkim z góry!

Odpowiedzi:

2 dla odpowiedzi № 1

Aby uzyskać wynik, musisz albo PIVOT dwa razy, albo UNPIVOT Dollars i Lbs kolumny w jednej kolumnie, a następnie zastosuj PIVOT jeden raz. Wolę odłączyć, a następnie przestawić, ponieważ uważam, że jest to o wiele łatwiejsze.

Zamiast najpierw pracować dynamicznie, powinieneśnapisz zapytanie w wersji statycznej lub na stałe, aby uzyskać poprawną logikę, a następnie przekonwertuj je na dynamiczny SQL. Przykład, który mam, wykorzystuje twoje ostateczne daty 201-01-31, itp., ponieważ używasz funkcji do tworzenia tych dat i powinnaś być w stanie zastosować ją w razie potrzeby.

Ponieważ używasz programu SQL Server 2005+, możesz użyć polecenia KRZYŻ ZASTOSUJ, aby cofnąć przestawienie Dollars i Lbs. Kod będzie podobny do następującego:

select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + "_"+ c.col,
c.value
from yourtable t
cross apply
(
select "Dollars", Dollars union all
select "Lbs", Lbs
) c (col, value);

Widzieć SQL Fiddle with Demo. Konwertuje to twoje dane do następującego formatu:

| ITEMNO |            NEW_COL | VALUE |
|--------|--------------------|-------|
|      A | 2014-01-31_Dollars |     1 |
|      A |     2014-01-31_Lbs |     1 |
|      B | 2014-01-31_Dollars |     2 |
|      B |     2014-01-31_Lbs |     2 |
|      A | 2014-01-31_Dollars |     3 |

Połączyłem się z new_col ostateczne nazwy kolumn, których będziesz potrzebować. Ponownie możesz sformatować datę w dowolnym formacie, którego potrzebowałem, właśnie użyłem 2014-01-31 i dodał Dollars lub Lbs do końca. Po uzyskaniu danych PRZESUNIĘĆ wartości do końcowego pożądanego wyniku:

select ItemNo,
[2014-01-31_Lbs], [2014-01-31_Dollars],
[2014-02-28_Lbs], [2014-02-28_Dollars]
from
(
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + "_"+ c.col,
c.value
from yourtable t
cross apply
(
select "Dollars", Dollars union all
select "Lbs", Lbs
) c (col, value)
) d
pivot
(
sum(value)
for new_col in ([2014-01-31_Lbs], [2014-01-31_Dollars],
[2014-02-28_Lbs], [2014-02-28_Dollars])
) p;

Widzieć SQL Fiddle with Demo. Teraz masz pożądany wynik, więc po prostu przekonwertuj go na dynamiczny SQL:

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

select @cols = STUFF((SELECT "," + QUOTENAME(convert(varchar(10), t.[invoice date], 120) + "_"+ c.col)
from yourtable t
cross apply
(
select "Lbs", 0 union all
select "Dollars", 1
) c (col, so)
group by [invoice date], col, so
order by [invoice date], so
FOR XML PATH(""), TYPE
).value(".", "NVARCHAR(MAX)")
,1,1,"")


set @query = "SELECT ItemNo," + @cols + "
from
(
select
t.ItemNo,
new_col = convert(varchar(10), t.[invoice date], 120) + ""_""+ c.col,
c.value
from yourtable t
cross apply
(
select ""Dollars"", Dollars union all
select ""Lbs"", Lbs
) c (col, value)
) d
pivot
(
sum(value)
for new_col in (" + @cols + ")
) p "

exec sp_executesql @query;

Widzieć SQL Fiddle with Demo. Daje to końcowy wynik:

| ITEMNO | 2014-01-31_LBS | 2014-01-31_DOLLARS | 2014-02-28_LBS | 2014-02-28_DOLLARS |
|--------|----------------|--------------------|----------------|--------------------|
|      A |              4 |                  4 |              5 |                  5 |
|      B |              6 |                  6 |              6 |                  6 |

0 dla odpowiedzi nr 2

Oto twoja przykładowa tabela

  CREATE TABLE #TEMP([Invoice Date] DATE,[Item #] VARCHAR(10),[DollarS] NUMERIC(10,0),[Lbs.] NUMERIC(10,0))
INSERT INTO #TEMP VALUES ("1/1/14", "A",1,1)
INSERT INTO #TEMP VALUES ("1/2/14", "B",2,2)
INSERT INTO #TEMP VALUES ("1/3/14", "A",3,3)
INSERT INTO #TEMP VALUES ("1/4/14", "B",4,4)
INSERT INTO #TEMP VALUES ("2/1/14", "A",5,5)
INSERT INTO #TEMP VALUES ("2/1/14", "B",6,6)

Teraz musisz złożyć wniosek UNION ALL(zamiast UNPIVOT) i doprowadzić kolumny do wiersza i połączyć kolumny, uzyskać kolejność kolumn jak Date+LBS/DOLLARS.

SELECT DISTINCT DENSE_RANK() OVER(ORDER BY  CAST(LASTDAY AS DATE),UNIT DESC)RNO,*,
CAST(DATEPART(MONTH,LASTDAY)AS VARCHAR) +"/"+ CAST(DATEPART(DAY,LASTDAY)AS VARCHAR) +"/" +RIGHT(CAST(YEAR(LASTDAY)AS VARCHAR),2)+" " +UNIT  PIVOTCOL
INTO #NEWTABLE
FROM
(
SELECT [Item #],"DOLLARS" UNIT,
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))LASTDAY,
SUM([Dollars]) OVER(PARTITION BY [Item #],DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))) VALUE
FROM #TEMP

UNION ALL

SELECT [Item #], "LBS.",
DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))LASTDAY,
SUM([Lbs.]) OVER(PARTITION BY [Item #],DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,[Invoice Date])+1,0))) DOLLARSUM
FROM #TEMP
)TAB

Teraz zadeklaruj zapytanie, aby dynamicznie uzyskać kolumny i ustawić NULL to Zero

DECLARE @cols NVARCHAR (MAX)
DECLARE @NullToZeroCols NVARCHAR (MAX)

SELECT @cols = COALESCE (@cols + ",[" + PIVOTCOL + "]",
"[" + PIVOTCOL + "]")
FROM    (SELECT DISTINCT RNO,PIVOTCOL FROM #NEWTABLE) PV
ORDER BY RNO
PRINT @COLS

SET @NullToZeroCols = SUBSTRING((SELECT ",ISNULL(["+PIVOTCOL+"],0) AS ["+PIVOTCOL+"]"
FROM(SELECT DISTINCT RNO,PIVOTCOL FROM #NEWTABLE GROUP BY RNO,PIVOTCOL)TAB
ORDER BY RNO  FOR XML PATH("")),2,8000)

Teraz przestaw zapytanie

DECLARE @query NVARCHAR(MAX)
SET @query = "SELECT [Item #]," + @NullToZeroCols + " FROM
(
SELECT [Item #],VALUE,PIVOTCOL FROM #NEWTABLE
) x
PIVOT
(
SUM(VALUE)
FOR PIVOTCOL IN (" + @cols + ")
) p
ORDER BY [Item #];"

EXEC SP_EXECUTESQL @query

WYNIK

wprowadź opis obrazu tutaj