跨不同行的值 - 将它们组合成 1 行
Posted
技术标签:
【中文标题】跨不同行的值 - 将它们组合成 1 行【英文标题】:Values Across Different Rows - combine them into 1 row 【发布时间】:2022-01-01 05:00:50 【问题描述】:我在一个表中有以下结果,我需要得到的是频率列向下到适当的行,因此频率列中的 NULL 值被替换为适当的值。注意 CustomerCode 不同的值。
任何人都知道如何做到这一点,并在这样做时删除没有 Type 和 TypeAmount 的频率行?
CustomerCode Frequency Type TypeAmount
C12345 Monthly NULL NULL
C12345 NULL A1 5.00
C12345 NULL A2 20.00
C12345 Fortnightly NULL NULL
C12345 NULL A1 5.00
C12345 NULL A2 20.00
C56789 Fortnightly NULL NULL
C56789 NULL A1 50.00
期望的输出
CustomerCode Frequency Type TypeAmount
C12345 Monthly A1 5.00
C12345 Monthly A2 20.00
C12345 Fortnightly A1 5.00
C12345 Fortnightly A2 20.00
C56789 Fortnightly A1 50.00
样本数据
Create Table #Data
(
CustomerCode varchar(50),
Frequency varchar(50) NULL,
Type varchar(50) NULL,
TypeAmount money NULL
)
insert into #Data
(
CustomerCode,
Frequency,
Type,
TypeAmount
)
select
'C12345',
'Monthly',
NULL,
NULL
union all
select
'C12345',
NULL,
'A1',
'5.00'
union all
select
'C12345',
NULL,
'A2',
'20.00'
union all
select
'C12345',
'Fornightly',
NULL,
NULL
union all
select
'C12345',
NULL,
'A1',
'5.00'
union all
select
'C12345',
NULL,
'A2',
'20.00'
union all
select
'C56789',
'Fornightly',
NULL,
NULL
union all
select
'C56789',
NULL,
'A1',
'50.00'
select * from #Data
【问题讨论】:
所需的输出会有所帮助。 @Arun - 当然,完成 我已经发布了答案。看看吧! 这行得通吗? 感谢@arun - 我目前正在测试,8 分钟后它仍在针对真实数据运行。 【参考方案1】:您的数据必须有一些已定义的顺序,否则您将无法执行此查询。我通过将您的数据插入到带有标识列的临时表中来创建订单以供您参考。我假设您的源数据中定义了一些基本顺序。只需将其替换为我的代理键列 [ID]
DROP TABLE IF EXISTS #Data
Create Table #Data
(
ID int Identity(1,1),
CustomerCode varchar(50),
Frequency varchar(50) NULL,
Type varchar(50) NULL,
TypeAmount money NULL
)
INSERT INTO #Data (CustomerCode,Frequency,[Type],TypeAmount )
VALUES ('C12345','Monthly',NULL,NULL)
,('C12345',NULL,'A1','5.00')
,('C12345',NULL,'A2','20.00')
,('C12345','Fornightly',NULL,NULL)
,('C12345',NULL,'A1','5.00')
,('C12345',NULL,'A2','20.00')
,('C56789','Fornightly',NULL,NULL)
,('C56789',NULL,'A1','50.00')
Select *
FROM #Data
SELECT A.ID
,B.Frequency
,A.Type
,A.TypeAmount
from #Data as A
Cross Apply
( /*Grab most recent preceding row that has frequency populated*/
SELECT Top (1) DTA.Frequency
From #Data AS DTA
Where A.CustomerCode = DTA.CustomerCode
AND DTA.ID < A.ID
AND DTA.Frequency IS NOT NULL
Order by DTA.ID DESC
) AS B
WHERE A.Frequency IS NULL
如果性能是一个问题,建议在执行您的选择之前创建一个这样的索引:
Create Index ix on #Data(CustomerCode,ID) Include (Frequency)
【讨论】:
【参考方案2】:RECURSIVE CTE
应该可以解决问题:
With cte AS
(
SELECT customerCode, frequency, type, TypeAmount, rn
FROM
(
SELECT *, ROW_NUMBER()OVER(PARTITION BY CustomerCode ORDER BY CustomerCode) AS rn
FROM #data
) AS d
WHERE Frequency IS NOT NULL
UNION ALL
SELECT d2.customerCode, cte.frequency, d2.type, d2.TypeAmount, d2.rn
From
(
SELECT *, ROW_NUMBER()OVER(PARTITION BY CustomerCode ORDER BY CustomerCode) AS rn
FROM #data
) AS d2
INNER JOIN cte
ON d2.rn=cte.rn+1
AND d2.CustomerCode=cte.CustomerCode
WHERE d2.Frequency IS NULL
)
SELECT *
FROM cte
WHERE Type IS NOT NULL
AND TypeAmount IS NOT NULL
ORDER BY CustomerCode, rn;
结果:
customerCode | frequency | type | TypeAmount | rn |
---|---|---|---|---|
C12345 | Monthly | A1 | 5.0000 | 2 |
C12345 | Monthly | A2 | 20.0000 | 3 |
C12345 | Fornightly | A1 | 5.0000 | 5 |
C12345 | Fornightly | A2 | 20.0000 | 6 |
C56789 | Fornightly | A1 | 50.0000 | 2 |
查询说明:
-
创建
rownumber
,以便我们可以参考上一行来获取frequency
CTE
的第一部分将获取 NON NULL
频率,第二部分将获取 NULL
频率
将两者与前 1 行连接以获得各自的Frequency
在此处查看DEMO
【讨论】:
感谢 Arun,它似乎产生了正确的结果。但是,当它针对数万条记录运行时,它非常慢。您可以提出任何更改建议吗? @Philip,我想这不能更快,因为我们在可能的地方使用了 JOINS。但是,创建ROW_NUMBER
可以在CTE开始之前完成(这样子查询会减少)
PARTITION BY CustomerCode ORDER BY CustomerCode
是完全不确定的,每次可能返回不同的结果。它工作一次或一百次的事实并不意味着它会一直工作。您需要另一列来排序
查理脸是正确的,需要在数据上定义顺序。 @Arun 不需要递归,它引入了不必要的复杂性和性能挑战。只需要一个简单的 CROSS APPLY 来查找最后填充的频率值(见我的答案)【参考方案3】:
;with cte1 as
(select CustomerCode,Frequency,ROW_NUMBER()over( order by
customercode)rn,Type,TypeAmount from #Data a ),
cte2 as ( select * from cte1 where Frequency is not null)
select row_number()over(order by
cte2.rn)sno,cte1.CustomerCode,cte2.Frequency,cte1.Type,cte1.TypeAmount from cte1
inner join cte2 on cte1.CustomerCode=cte2.CustomerCode
where cte1.Type is not null
group by cte1.CustomerCode,cte2.Frequency,cte1.Type,cte1.TypeAmount,cte2.rn
order by 1
【讨论】:
以上是关于跨不同行的值 - 将它们组合成 1 行的主要内容,如果未能解决你的问题,请参考以下文章