rah*_*rma 7 sql-server-2005 subquery running-total left-join
我必须在我的应用程序中显示总计列的运行总数...所以我使用以下查询来查找运行总计...我发现两者都按照我的需要工作.在一个我使用左连接与group by和另一个我使用子查询.
现在我的问题是,当我的数据每天增加数千时,哪一个更快,如果数据将限制在1000或2000行,那么哪一个更好......而且任何其他方法比这两个更快? ??
declare @tmp table(ind int identity(1,1),col1 int)
insert into @tmp
select 2
union
select 4
union
select 7
union
select 5
union
select 8
union
select 10
SELECT t1.col1,sum( t2.col1)
FROM @tmp AS t1 LEFT JOIN @tmp t2 ON t1.ind>=t2.ind
group by t1.ind,t1.col1
select t1.col1,(select sum(col1) from @tmp as t2 where t2.ind<=t1.ind)
from @tmp as t1
Run Code Online (Sandbox Code Playgroud)
计算SQL Server中运行总计的一个很好的资源是Itzik Ben Gan的这份文档,该文档作为其竞选活动的一部分提交给SQL Server团队,以使该OVER子句从其最初的SQL Server 2005实现进一步扩展.在其中,他展示了一旦你进入成千上万行游标执行基于集合的解决方案.SQL Server 2012确实扩展了该OVER子句,使得这种查询变得更加容易.
SELECT col1,
SUM(col1) OVER (ORDER BY ind ROWS UNBOUNDED PRECEDING)
FROM @tmp
Run Code Online (Sandbox Code Playgroud)
正如您在SQL Server 2005上一样,但是您无法使用它.
Adam Machanic 在这里展示了如何使用CLR来改进标准TSQL游标的性能.
对于此表定义
CREATE TABLE RunningTotals
(
ind int identity(1,1) primary key,
col1 int
)
Run Code Online (Sandbox Code Playgroud)
我在数据库中创建了包含2,000行和10,000行的表,并且在ALLOW_SNAPSHOT_ISOLATION ON此设置中创建了一个(原因是我的初始结果是在数据库中设置了导致结果令人费解的方面).
所有表的聚簇索引只有1个根页.每个叶页的数量如下所示.
+-------------------------------+-----------+------------+
| | 2,000 row | 10,000 row |
+-------------------------------+-----------+------------+
| ALLOW_SNAPSHOT_ISOLATION OFF | 5 | 22 |
| ALLOW_SNAPSHOT_ISOLATION ON | 8 | 39 |
+-------------------------------+-----------+------------+
Run Code Online (Sandbox Code Playgroud)
我测试了以下案例(链接显示执行计划)
包含额外CTE选项的原因是为了提供CTE解决方案,如果ind不保证色谱柱顺序排列,该解决方案仍然有效.
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
DECLARE @col1 int, @sumcol1 bigint;
WITH RecursiveCTE
AS (
SELECT TOP 1 ind, col1, CAST(col1 AS BIGINT) AS Total
FROM RunningTotals
ORDER BY ind
UNION ALL
SELECT R.ind, R.col1, R.Total
FROM (
SELECT T.*,
T.col1 + Total AS Total,
rn = ROW_NUMBER() OVER (ORDER BY T.ind)
FROM RunningTotals T
JOIN RecursiveCTE R
ON R.ind < T.ind
) R
WHERE R.rn = 1
)
SELECT @col1 =col1, @sumcol1=Total
FROM RecursiveCTE
OPTION (MAXRECURSION 0);
Run Code Online (Sandbox Code Playgroud)
添加了所有查询CAST(col1 AS BIGINT)以避免运行时出现溢出错误.另外,对于所有这些,我将结果分配给上面的变量,以便消除从考虑中发回结果所花费的时间.
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| | | | Base Table | Work Table | Time |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| | Snapshot | Rows | Scan count | logical reads | Scan count | logical reads | cpu | elapsed |
| Group By | On | 2,000 | 2001 | 12709 | | | 1469 | 1250 |
| | On | 10,000 | 10001 | 216678 | | | 30906 | 30963 |
| | Off | 2,000 | 2001 | 9251 | | | 1140 | 1160 |
| | Off | 10,000 | 10001 | 130089 | | | 29906 | 28306 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| Sub Query | On | 2,000 | 2001 | 12709 | | | 844 | 823 |
| | On | 10,000 | 2 | 82 | 10000 | 165025 | 24672 | 24535 |
| | Off | 2,000 | 2001 | 9251 | | | 766 | 999 |
| | Off | 10,000 | 2 | 48 | 10000 | 165025 | 25188 | 23880 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| CTE No Gaps | On | 2,000 | 0 | 4002 | 2 | 12001 | 78 | 101 |
| | On | 10,000 | 0 | 20002 | 2 | 60001 | 344 | 342 |
| | Off | 2,000 | 0 | 4002 | 2 | 12001 | 62 | 253 |
| | Off | 10,000 | 0 | 20002 | 2 | 60001 | 281 | 326 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
| CTE Alllows Gaps | On | 2,000 | 2001 | 4009 | 2 | 12001 | 47 | 75 |
| | On | 10,000 | 10001 | 20040 | 2 | 60001 | 312 | 413 |
| | Off | 2,000 | 2001 | 4006 | 2 | 12001 | 94 | 90 |
| | Off | 10,000 | 10001 | 20023 | 2 | 60001 | 313 | 349 |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
Run Code Online (Sandbox Code Playgroud)
相关子查询和GROUP BY版本都使用"三角形"嵌套循环连接,由RunningTotals表(T1)上的聚簇索引扫描驱动,并且对于该扫描返回的每一行,寻求返回到表(T2)自连接T2.ind<=T1.ind.
这意味着重复处理相同的行.当T1.ind=1000行被处理的自连接检索和资金的所有行有ind <= 1000,则在下一行T1.ind=1001相同的1000个检索行再次和一个附加行等一起总结.
2,000行表的此类操作的总数为2,001,000,一般为10k行50,005,000或更多(n² + n) / 2 ,其明显呈指数增长.
在2000行的情况下的主要区别GROUP BY和子查询的版本是,前者具有流聚合后加入等具有三列供给到它(T1.ind,T2.col1,T2.col1)和一个GROUP BY的属性T1.ind,而后者被称为一个标量集合计算,在连接之前使用流聚合,只T2.col1进入它并且根本没有GROUP BY设置属性.可以看出,这种更简单的布置在减少CPU时间方面具有可测量的益处.
对于10,000行的情况,子查询计划中存在另外的差异.它添加了一个急切的假脱机,将所有ind,cast(col1 as bigint)值复制到其中tempdb.在快照隔离开启的情况下,这比聚集索引结构更紧凑,并且净效果是将读取次数减少大约25%(因为基表为版本控制信息保留了相当多的空白空间),当此选项关闭时,它会变得不那么紧凑(可能是因为bigintvs int差异)并且读取结果更多.这减少了子查询和组之间的差距,但子查询仍然获胜.
然而,明显的赢家是递归CTE.对于"无间隙"版本,来自基表的逻辑读取现在2 x (n + 1)反映n索引搜索到2级索引以检索所有行加上最后一个不返回任何行并终止递归的行.这仍然意味着20,002次读取处理22页表!
递归CTE版本的逻辑工作表读取非常高.似乎每个源行有6个工作表读取.这些来自存储前一行随后的输出指数阀芯再次下一次迭代中(这由Umachandar贾雅彻德伦很好的解释看这里).尽管数量很高,但这仍然是表现最好的.
| 归档时间: |
|
| 查看次数: |
2614 次 |
| 最近记录: |