使用CTE提供累计总数

时间:2015-03-10 13:07:10

标签: sql-server sql-server-2008 sql-server-2012 common-table-expression

我的桌子上有一些名字:

SELECT * FROM d;

Forename
--------------------------------
Robert
Susan
Frances
Kate
May
Alex
Anna

我想按字母顺序提取累计名称长度。到目前为止,我有:

WITH    Names ( RowNum, Forename, ForenameLength )
          AS ( SELECT   ROW_NUMBER() OVER ( ORDER BY forename ) AS RowNum ,
                        Forename ,
                        LEN(forename) AS ForenameLength
               FROM     d
             )
    SELECT  RowNum ,
            Forename ,
            ForenameLength ,
            ISNULL(ForenameLength + ( SELECT    ISNULL(SUM(ForenameLength),0)
                                      FROM      Names
                                      WHERE     RowNum < n.RowNum
                                    ), 0) AS CumLen
    FROM    NAMES n;

RowNum               Forename                         ForenameLength CumLen
-------------------- -------------------------------- -------------- -----------
1                    Alex                             4              4
2                    Anna                             4              8
3                    Frances                          7              15
4                    Kate                             4              19
5                    May                              3              22
6                    Robert                           6              28
7                    Susan                            5              33

但我明白在CTE中应该可以(递归地)这样做。有谁知道如何实现这一目标?

N.B。虽然我们正在开发2012年,但目前的实时系统是2008年,所以任何解决方案都需要至少在短期内向后兼容。

1 个答案:

答案 0 :(得分:5)

您使用的是SQL Server 2012,应使用sum() over()代替。

select row_number() over(order by d.Forename) as RowNum,
       d.Forename,
       len(d.Forename) as ForenameLength,
       sum(len(d.Forename)) over(order by d.Forename rows unbounded preceding) as CumLen
from d
order by d.Forename;

结果:

RowNum   Forename     ForenameLength CumLen
-------- ------------ -------------- -----------
1        Alex         4              4
2        Anna         4              8
3        Frances      7              15
4        Kate         4              19
5        May          3              22
6        Robert       6              28
7        Susan        5              33

更新

如果由于某种原因绝对需要递归版本,它可能看起来像这样:

with C as
(
  select top(1)
    1 as RowNum,
    d.Forename,
    len(d.Forename) as ForenameLength,
    len(d.Forename) as CumLen
  from d
  order by d.Forename      
  union all
  select d.RowNum,
         d.Forename,
         d.ForenameLength,
         d.CumLen      
  from (
       select C.RowNum + 1 as RowNum,
              d.Forename,
              len(d.Forename) as ForenameLength,
              C.CumLen + len(d.Forename) as CumLen,
              row_number() over(order by d.ForeName) as rn
       from d
         inner join C
           on C.Forename < d.Forename
       ) as d
  where d.rn = 1
)
select C.RowNum,
       C.Forename,
       C.ForenameLength,
       C.CumLen
from C;

改编自Paul White的Performance Tuning the Whole Query Plan

相关问题