问题描述
我有 3 列:ID(Varchar 类型)、Age(INT 类型)和金额($)。真正的表是 100K 行:该表列出了客户在每个年龄段收到的每一笔金额。
ID | 年龄 | 金额 |
---|---|---|
A01 | 1 | 10 |
A01 | 2 | 11 |
A01 | 3 | 12 |
A02 | 90 | 50 |
A02 | 100 | 51 |
A02 | 110 | 52 |
我需要将每个客户的每个年龄增加到 120 并得到这样的结果
ID | 年龄 | 金额 |
---|---|---|
A01 | 1 | 10 |
A01 | 2 | 11 |
A01 | 3 | 12 |
A01 | 4 | 13 |
A01 | n+1 | ... |
A01 | 120 | 1500 |
A02 | 90 | 50 |
A02 | 100 | 51 |
A02 | 110 | 52 |
A02 | 111 | 53 |
A02 | n+1 | ... |
A02 | 120 | 600 |
这是我第一次做循环,经过多次尝试,这是我能写的最好的,但它不起作用。
SELECT disTINCT [id],[Age],ROW_NUMBER() OVER (PARTITION BY [id] ORDER BY
[Age])AS Rnk INTO tableRanked FROM MyTable
DECLARE @Agefirst AS INT
DECLARE @Agelast AS INT
DECLARE @AgeCurent AS INT
DECLARE @id as nvarchar(max)
SET @Agelast = 120
SET @id = 0
SET @AgeCurent = 0
WHILE(@AgeCurent <= @Agelast)
BEGIN
SET @AgeCurent = @AgeCurent+1
INSERT INTO tableRanked ([Id],[Age])
SELECT [Rnk],[Id],[Age] FROM tableRanked
SET @id = @id+1
END
非常感谢!
解决方法
您可以使用 numbers function,or tally table 代替循环(或递归 CTE,它也是一个循环)。 Numbers 函数使用的资源很少,而且速度非常快。从您的代码看来,每个 ID 的年龄范围应介于 1 到 120 岁之间。 像这样
;with unq_id(id) as (
select distinct id
from MyTable)
insert into tableRanked
select ui.id as ID,fn.n as Age,isnull(m.Amount,0) Amount
from unq_id ui
cross join dbo.fnTally(1,120) fn
left join MyTable m on ui.id=m.Id
and fn.n=m.Age
order by ui.id,fn.n;
,
我不明白如果每个增加 1,最终金额怎么会是 1500 或 600。但是,如果我认为这是问题中的错误并将其放在一边,我建议您可以在此处使用带有递归 CTE 的 INSERT ... SELECT ...
。那么就没有必要在这里摆弄程序代码了。
WITH cte
AS
(
SELECT 1 is_anchor,x.id,x.age,x.amount FROM
(SELECT r.id,r.age,r.amount,row_number() OVER (PARTITION BY r.id
ORDER BY r.age DESC) r
FROM tableranked r) x
WHERE x.r = 1
UNION ALL
SELECT 0 is_anchor,c.id,c.age + 1,c.amount + 1
FROM cte c
WHERE c.age + 1 <= 120
)
INSERT INTO tableranked
(id,age,amount)
SELECT c.id,c.age,c.amount
FROM cte c
WHERE c.is_anchor = 0
OPTION(MAXRECURSION 120);
您可以使用 row_number()
获取锚行,这些是每个 ID 具有最大年龄的行。将它们标记为锚行以在 SELECT
的 INSERT ... SELECT ...
部分将它们过滤掉并且不再插入它们。