Sql server 多栏透视
我正试图在SQLServer2008中的发票表上透视两列。因此,我有如下数据:Sql server 多栏透视,sql-server,sql-server-2008,pivot,unpivot,Sql Server,Sql Server 2008,Pivot,Unpivot,我正试图在SQLServer2008中的发票表上透视两列。因此,我有如下数据: +--------------+--------+---------+------+ | Invoice Date | Item # | Dollars | Lbs. | +--------------+--------+---------+------+ | 1/1/14 | A | 1 | 1 | | 1/2/14 | B | 2 |
+--------------+--------+---------+------+
| 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 |
+--------------+--------+---------+------+
我想把它显示为
+--------+--------------+-----------------+--------------+-----------------+
| 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 |
+--------+--------------+-----------------+--------------+-----------------+
请注意,列名是该月的最后一天,可以是美元,也可以是英镑。我可以只做一列(英镑或美元),但我不能同时做两列
以下是我的示例代码,仅用于磅:
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'
提前谢谢大家 为了获得结果,您必须旋转两次,或者将
美元
和磅
列取消分割为一列,然后应用旋转一次。我的偏好是先取消pivot,然后再进行pivot,因为我发现这要容易得多
与首先动态工作不同,您应该将查询作为静态或硬编码版本来编写,以获得正确的逻辑,然后将其转换为动态SQL。我举的例子使用了您的最终日期201-01-31
,等等,因为您正在使用一个函数来创建这些日期,并且应该能够根据需要应用这些日期
由于您使用的是SQL Server 2005+,因此可以使用交叉应用程序取消PIVOT美元
和磅
。代码将类似于以下内容:
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);
看。这会将数据转换为以下格式:
| 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 |
我已将您需要的最终列名连接到new\u col
中。同样,您可以按照您需要的任何格式设置日期,我只是使用了2014-01-31
,并在其末尾添加了美元
或磅
。获得数据后,您将把这些值转化为最终的预期结果:
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;
看。现在您已经得到了想要的结果,只需将其转换为动态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;
看。这将给出以下的最终结果:
| 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 |
这是您的样品台
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)
现在您需要应用UNION ALL
(而不是UNPIVOT
),将列带到行中并合并列,获得列的顺序为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
现在声明查询以动态获取列,并将NULL设置为零
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)
现在旋转查询
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
是的。你是对的。这就是为什么我提到“我使用UNION ALL而不是UNPIVOT”@bluefeetI添加了代码,将pivot中的空值替换为零。如果您想要空值而不是零,请随意问我。我用一种简单的方式编写了代码@梅森·波尔曼