Estoy tratando de pivotar en dos columnas en SQL Server 2008 en una tabla de facturas. Así que tengo datos como los siguientes:
+--------------+--------+---------+------+
| 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 |
+--------------+--------+---------+------+
Me gustaría mostrarlo como
+--------+--------------+-----------------+--------------+-----------------+
| 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 |
+--------+--------------+-----------------+--------------+-----------------+
Tenga en cuenta que el nombre de la columna es el último día de ese mes y en dólares o libras. Puedo hacerlo solo una columna (ya sea en libras o en dólares) sin embargo no puedo hacerlo en ambas.
Aquí está mi código de ejemplo para sólo libras:
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"
¡Gracias a todos de antemano!
Respuestas
2 para la respuesta № 1Para obtener el resultado, deberá PIVOTAR dos veces o UNPIVOT el Dollars
y Lbs
columnas en una sola columna y luego aplicar el PIVOT una vez. Mi preferencia sería quitar el pivote y luego girar porque me resulta mucho más fácil.
En lugar de trabajar dinámicamente primero, deberíasescriba la consulta como una versión estática o codificada para obtener la lógica correcta, luego conviértala a SQL dinámico. El ejemplo que tengo usa tus fechas finales. 201-01-31
, etc. porque está utilizando una función para crear esas fechas y debe poder aplicarla según sea necesario.
Ya que está utilizando SQL Server 2005+, puede usar CROSS APPLY para quitar la imagen Dollars
y Lbs
. El código será similar al siguiente:
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);
Ver SQL Fiddle con demostración. Esto convierte sus datos al siguiente formato:
| 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 |
He concatenado en new_col
los últimos nombres de columna que necesitará. Una vez más, puede formatear la fecha en el formato que necesite, que acabo de usar 2014-01-31
y agregó el Dollars
o Lbs
hasta el final de ello. Una vez que haya obtenido los datos, PIVOTARÁ los valores en su resultado final deseado:
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;
Ver SQL Fiddle con demostración. Ahora tiene el resultado que desea, así que simplemente conviértalo a SQL dinámico:
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;
Ver SQL Fiddle con demostración. Esto da un resultado final de:
| 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 para la respuesta № 2
Aquí está su tabla de muestra
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)
Ahora necesitas aplicar UNION ALL
(en lugar de UNPIVOT
) y coloque las columnas en fila y combine las columnas, obtenga el orden de las columnas como 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
Ahora declare la consulta para obtener las columnas dinámicamente y para establecer 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)
Ahora pivota la consulta
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
RESULTADO