我的一位同事有一个问题,我正在努力帮助他。
他有一个SQL视图,其中包含以下数据(示例数据(:-
Category Value
Cat A 10
Cat A 20
Cat A 30
Cat B 15
Cat B 15
Cat C 10
Cat C 10
他想向视图中添加一个列,该列保留"值"列的运行总计。
当类别更改时,此列必须重置汇总。
所以输出数据必须像这样:-
Category Value Running
Cat A 10 10
Cat A 20 30
Cat A 30 60
Cat B 15 15
Cat B 15 30
Cat C 10 10
Cat C 10 20
我们可以像这样将表连接到自身上来获得运行总计:-
select t1.id, t1.[count], SUM(t2.[count]) as sum
from TableA t1
inner join TableA t2 on t1.id >= t2.id
group by t1.id, t1.[count]
order by t1.id
问题是我们没有 ID 列,我们如何指示在类别更改时重置运行总计?
这在较大的表上不会表现得特别好,但可以完成工作!
select 'Cat A' as class,10 as value into #x
UNION ALL SELECT 'Cat A',20
UNION ALL SELECT 'Cat A',30
UNION ALL SELECT 'Cat B',15
UNION ALL SELECT 'Cat B',15
UNION ALL SELECT 'Cat C',10
UNION ALL SELECT 'Cat C',10
;WITH running_total AS
(
select *
,ROW_number() OVER (PARTITION BY class order by value ASC) as row
from #x
)
SELECT
r1.class
,MAX(r1.value) as value
,SUM(r2.value) as running_total
FROM running_total r1
LEFT OUTER JOIN running_total r2 on r2.class = r1.class
AND r2.row <= r1.row
GROUP BY
r1.class
,r1.row
这通过使用公用表表达式 (CTE( 和窗口函数来工作。CTE 的工作方式与子查询类似,其中第一部分(称为 running_total(根据值向每个类添加一个基于行的 ID。
Row_number(( 的工作方式如此处所述,但基本上它会根据您定义分区的方式(本质上与分组依据相同,但不必在主查询中进行分组(和按类的内置顺序自动递增。
在此示例中,partitionign by class 确保每个新类别名称的最小值设置为 1 - 如果您想要不基于特定类别的总运行总计,则可以删除子句的这一部分。
CTE 的第二部分从 CTE 的第一部分选择结果,并在类匹配的地方加入自身。通过在 r2.row <= r1.row 上连接,这可确保第二个连接包含当前行
您可以将ROW_NUMBER()
函数与OUTER APPLY
结合使用
DECLARE @T TABLE (Category VARCHAR(5), Value INT)
INSERT INTO @T VALUES
('Cat A', 10),
('Cat A', 20),
('Cat A', 30),
('Cat B', 15),
('Cat B', 15),
('Cat C', 10),
('Cat C', 10)
;WITH T AS
( SELECT Category, Value, ROW_NUMBER() OVER(PARTITION BY Category ORDER BY Value) [RowNumber]
FROM @T
)
SELECT T1.Category,
T1.Value,
RunningTotal
FROM T T1
OUTER APPLY
( SELECT SUM(Value) [RunningTotal]
FROM T T2
WHERE T2.Category = T1.Category
AND T2.RowNumber <= T1.RowNumber
) RunningTotal