我有一个包含'Name','Amount'和'ReasonId'列的查询。我想总结金额并将原因放在一行,以便将每个名字保持为一行。大约有50个不同的ReasonId,所以我不想将列命名为ReasonId的名称。相反,我想将列命名为“Reason1”,“Reason2”,“Reason3”和“Reason4”。一个名称最多可以有4个不同的原因。
我有这个:
Name Amount ReasonId
-------------------------
Bob $5 7
Bob $8 6
John $2 8
John $5 9
John $3 9
John $8 4
我想制作以下内容:
Name Amount Reason1 Reason2 Reason3 Reason4
-----------------------------------------------------
Bob $13 7 6 NULL NULL
John $18 8 9 4 NULL
答案 0 :(得分:0)
一种方法是使用dense_rank
窗口函数对行进行编号,然后使用条件聚合将原因放在正确的列中。
我无法看到任何可以提供原因列的特定顺序的内容,但是可能有一些列缺少提供订单?
with cte as (
select
name,
reasonid,
amount,
dense_rank() over (partition by name order by reasonid) rn
from your_table
)
select
name,
sum(amount) amount,
max(case when rn = 1 then reasonid end) reason1,
max(case when rn = 2 then reasonid end) reason2,
max(case when rn = 3 then reasonid end) reason3,
max(case when rn = 4 then reasonid end) reason4
from cte
group by name
如果您有一些列提供了您想要的订单,请更改order by
函数中使用的dense_rank
子句。
Sample SQL Fiddle(使用PG作为MSSQL似乎处于离线状态。)
上述查询的输出为:
| name | amount | reason1 | reason2 | reason3 | reason4 |
|------|--------|---------|---------|---------|---------|
| Bob | 13 | 6 | 7 | (null) | (null) |
| John | 18 | 4 | 8 | 9 | (null) |
答案 1 :(得分:0)
你也可以用一个支点来达到这个目的;如果你知道列可以在脚本中输入它们,但如果没有,你可以使用动态sql(有理由你可能想要避免使用动态解决方案)。
此路由的优点是您可以在表中输入列列表,然后对该表的更改将导致输出更改并更改所涉及的脚本。缺点是与动态SQL相关的所有缺点。
为了变体,这里是一个使用临时表来保存数据的动态SQL解决方案,因为提供了不同的可能性:
-- set up your data
CREATE TABLE #MyTab (Name VARCHAR(4), Amount INT, ReasonId INT)
CREATE TABLE #AllPossibleReasons (Id INT,Label VARCHAR(10))
INSERT #AllPossibleReasons
VALUES
(1,'Reason1')
,(2,'Reason2')
,(3,'Reason3')
,(4,'Reason4')
,(5,'Reason5')
,(6,'Reason6')
,(7,'Reason7')
,(8,'Reason8')
,(9,'Reason9')
INSERT #MyTab
VALUES
('Bob',7,7)
,('Bob',8,6)
,('John',2,8)
,('John',5,9)
,('John',3,9)
,('John',8,4)
-----------------------------------------------------------------------------
-- The actual query
DECLARE @ReasonList VARCHAR(MAX) = ''
DECLARE @SQL VARCHAR(MAX)
SELECT @ReasonList = @ReasonList + ',' + QUOTENAME(Label)
FROM #AllPossibleReasons
SET @ReasonList = SUBSTRING(@ReasonList,2,LEN(@ReasonList))
SET @SQL =
'SELECT Name,Value,' + @ReasonList + ' FROM
(SELECT
M.Name,SUM(Amount) AS This, Label, SUM(Total.Value) AS Value
FROM
#MyTab AS M
INNER JOIN #AllPossibleReasons AS Reason ON M.ReasonId = Reason.Id
INNER JOIN(SELECT T.Name, SUM(Amount)Value
FROM #MyTab T GROUP BY T.Name) AS Total ON M.Name = Total.Name
GROUP BY M.Name, Reason.Label) AS Up
PIVOT (SUM(THis) FOR Label IN (' + @ReasonList + ')) AS Pvt'
EXEC (@SQL)
DROP TABLE #AllPossibleReasons
DROP TABLE #MyTab
答案 2 :(得分:0)
根据ListAGG in SQLSERVER中的信息,我想出了一个有点丑陋的例子:
with tbl1 as (
-- Set up initial data set
select 'Bob' name, 5 amount, 7 ReasonId
union all select 'Bob' , 3, 4
union all select 'Bob', 2, 1
union all select 'Brian', 8, 2
union all select 'Bob', 6, 4
union all select 'Brian', 1, 3
union all select 'Tim', 2, 2)
, TBL2 AS ( -- Add a blank to separate the concatenation
SELECT NAME
, AMOUNT
, CAST(ReasonId as varchar) + ' ' ReasonId from tbl1
)
select ta.name
, Total
, ReasonIds from (
(select distinct name, stuff((select distinct '' + t2.ReasonId from tbl2 t2
where t1.name = t2.name
for xml path(''), type).value('.','NVARCHAR(MAX)'),1,0,' ') ReasonIds from tbl2 t1) ta
inner join ( select name, sum(amount) Total from tbl1 group by name) tb on ta.name = tb.name) ;
这会将TBL1转换为以下内容:
name Total ReasonIds
Bob 16 1 4 7
Brian 9 2 3
Tim 2 2