4

我的一位同事有一个问题,我正试图帮助他解决。

他有一个 SQL 视图,其中包含以下数据(示例数据):-

Category          Value
Cat A             10
Cat A             20
Cat A             30
Cat B             15
Cat B             15
Cat C             10
Cat C             10

他想在视图中添加一列,以保持 Value 列的运行总计。

当类别更改时,此列必须重置运行总计。

所以输出数据必须是这样的:-

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 列,当类别更改时,我们将如何指示累计重置?

4

2 回答 2

4

这在较大的桌子上不会表现得特别好,但会完成工作!

   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() 按此处记录的方式工作, 但基本上它会根据您定义分区的方式(与 group by 基本相同,但不必在主查询中分组)和按类的内置顺序自动递增。

在此示例中,按类划分确保每个新类别名称的最小值设置为 1 - 如果您想要一个不基于特定类别的总运行总数,您将删除该子句的这一部分。

CTE 的第二部分从 CTE 的第一部分中选择结果,并在类匹配的地方加入自身。通过加入 r2.row <= r1.row 这确保了第二个连接包括所有值 <= 当前行 - 即 R1 中的第 3 行将包括 R2 连接中的第 1、2、3 行。

于 2012-04-30T08:58:15.830 回答
3

您可以将该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
于 2012-04-30T09:00:20.560 回答