子查询或 leftjoin 与 group by 哪个更快?

2024-03-23

Query Execution Plani have to show running total with the total column in my application ... so i have used the following queries for finding the running total... and i find that both are working as per my need . in one i used the left join with group by and in another one i used the sub query .

现在我的问题是,当我的数据每天增长数千次时,哪一种更快,如果数据限制在 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

有关在 SQL Server 中计算运行总计的重要资源是这个文件 http://www.insidetsql.com/OVER_Clause_and_Ordered_Calculations.doc由 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 

然而,由于您使用的是 SQL Server 2005,因此您无法使用此功能。

亚当·马卡尼奇在这里显示 http://sqlblog.com/blogs/adam_machanic/archive/2006/07/12/running-sums-yet-again-sqlclr-saves-the-day.aspx如何使用 CLR 来提高标准 TSQL 游标的性能。

对于该表定义

CREATE TABLE RunningTotals
(
ind int identity(1,1) primary key,
col1 int
)

我在数据库中创建包含 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 |
+-------------------------------+-----------+------------+

我测试了以下案例(链接显示执行计划)

  1. 左连接和分组依据 https://i.stack.imgur.com/wo4Km.jpg
  2. 相关子查询2000行计划 https://i.stack.imgur.com/pvcXR.jpg,10000行计划 https://i.stack.imgur.com/VdQfC.jpg
  3. 来自 Mikael(更新)答案的 CTE https://i.stack.imgur.com/LDpyp.jpg
  4. 热膨胀系数低于 https://i.stack.imgur.com/dv2XE.jpg

包含附加 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);

所有的查询都有一个CAST(col1 AS BIGINT)添加以避免运行时溢出错误。此外,对于所有这些,我将结果分配给上述变量,以消除发送回结果所花费的时间。

Results

+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+
|                  |          |        |          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     |
+------------------+----------+--------+------------+---------------+------------+---------------+-------+---------+

相关子查询和GROUP BY版本使用由聚集索引扫描驱动的“三角形”嵌套循环连接RunningTotals table (T1) 并且,对于该扫描返回的每一行,重新查找表 (T2) 自加入T2.ind<=T1.ind.

这意味着相同的行会被重复处理。当。。。的时候T1.ind=1000行被处理,自连接检索所有行并将其求和ind <= 1000,然后对于下一行T1.ind=1001检索到相同的 1000 行again并与另外一行一起求和,依此类推。

对于 2,000 行的表,此类操作的总数为 2,001,000,对于 10k 行,此类操作的总数为 50,005,000 或更多(n² + n) / 2 http://mathworld.wolfram.com/ArithmeticSeries.html显然呈指数增长。

在 2,000 行的情况下,GROUP BY子查询版本是前者在连接后具有流聚合,因此有三列输入其中(T1.ind, T2.col1, T2.col1) and a GROUP BY的财产T1.ind而后者被计算为标量聚合,连接之前的流聚合仅具有T2.col1喂进去并且没有GROUP BY属性完全设置。可以看出,这种更简单的安排在减少 CPU 时间方面具有显着的好处。

对于 10,000 行的情况,子查询计划还有一个额外的差异。它添加了一个热切的线轴 http://technet.microsoft.com/en-us/library/ms190435.aspx它复制了所有的ind,cast(col1 as bigint)值转化为tempdb。在启用快照隔离的情况下,这比聚集索引结构更紧凑,最终效果是将读取次数减少约 25%(因为基表为版本控制信息保留了相当多的空白空间),当此选项关闭时,它的结构会不太紧凑(大概是由于bigint vs int差异)和更多的读取结果。这减少了子查询和 group by 版本之间的差距,但子查询仍然获胜。

然而,明显的赢家是递归 CTE。对于“无间隙”版本,现在从基表进行逻辑读取2 x (n + 1)反映了nindex 查找 2 级索引以检索所有行以及末尾的附加行,该行不返回任何内容并终止递归。然而,这仍然意味着要处理 22 页表需要 20,002 次读取!

递归 CTE 版本的逻辑工作表读取非常高。似乎每个源行有 6 次工作表读取。这些来自存储前一行输出的索引假脱机,然后在下一次迭代中再次读取(Umachandar Jayachandran 对此做了很好的解释)here http://www.eggheadcafe.com/software/aspnet/30627951/spools.aspx)。尽管数量很高,但这仍然是表现最好的。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

子查询或 leftjoin 与 group by 哪个更快? 的相关文章

  • 返回视图中两列之一 - 以不为空的一列为准

    我有一个包含三列的表 ColumnA ColumnB ColumnC AAA NULL 123 BBB 222 NULL CCC NULL NULL 我想创建一个 SELECT 语句 该语句将返回 ColumnA 然后创建第二列 该列将显示
  • 我需要 T-SQL 中的国家/地区列表

    我有一个国家 地区表 其中包含国家 地区名称列 我需要 DDL 中现成的列表来填充 CountryName 列 该表驻留在 SQL Server 2008 中 你可以尝试这个脚本 CREATE TABLE tbl Countries Cou
  • SQL Server 中的 DECODE() 函数

    SELECT PC COMP CODE R PC RESUB REF DECODE PC SL LDGR CODE 02 DR CR PC DEPT NO DEPT PC DEPT NO PC SL LDGR CODE PC SL ACNO
  • Access 是否有与 SQL Server NewId() 函数等效的函数?

    我编写了将数据加载到 SQL Server 数据库中的 SQL 语句 存储在文本文档中 这些陈述需要每天重复 一些语句使用NewId 函数来填充数据库中的键控字段 这工作得很好 当我正在编写应用程序来复制这些语句时 我想使用 Access
  • 在 IF .. ELSE 语句中使用临时表

    为什么SQL Server坚持认为临时表已经存在 其中之一将会发生 所以永远不会出现这种情况 declare checkvar varchar 10 declare tbl TABLE colx varchar 10 set checkva
  • 如何修复 SQL Server 中阿拉伯文与英文混合的反向字符

    我有一个 SQL Server 数据库 表列在同一字段中包含阿拉伯语和英语字符 就像Oracle 寻求您的帮助 将阿拉伯字符与英文字符分开以反转它们 因为阿拉伯字符在字段中没有特定的位置 开始 结束或中间 Edit这些字符来自旧版 IBM
  • 如何包含时间跨度内多个分组的缺失数据?

    我在下面引用了查询 其中按教师 学习年月和过去 12 个月 包括当月 的房间对学习计数进行分组 我得到的结果是正确的 但是 我想在数据丢失时包含计数为零的行 我查看了其他几个相关的帖子 但无法获得所需的输出 Postgres 如何返回缺失数
  • CTE - 递归更新数量直到消耗总量

    我一直在研究 CTE 试图确定是否可以使用订单数量递归更新库存数量记录 直到订单数量被消耗 以下是表格和记录 CREATE TABLE dbo myOrder Account float NOT NULL Item float NOT NU
  • 从 XML 转换为 SQL Server 日期时间时出现毫秒错误

    我遇到了将日期时间从 XML ISO8601 yyyy mm ddThh mi ss mmm 转换为 SQL Server 2005 日期时间相关的问题 问题是转换毫秒时错误 我已经使用 nvarchar 中的 Convert dateti
  • 获取同一 FK 日期差异的前一条记录

    我需要在一小时内插入同一客户的前 1 条记录 如果记录在一小时后插入 则不需要该记录 请参见下表 这只是数千条记录的样本 我正在使用 SQL Server 2005 替代文本 http img651 imageshack us img651
  • 在连接中使用聚合函数时如何使用 Group By 子句?

    我想连接三个表并计算表 A 的总和 数量 我尝试了一些东西 得到了想要的输出 但我仍然对聚合函数和 Group By 子句感到困惑 在通过连接两个或多个表来计算总和值时 我们需要在 Group By 子句中提及哪些列以及为什么需要给出这些列
  • MySQL 一对多转 JSON 格式

    我有两个 MySQL 表 User id name Sale id user item Where Sale user 是一个外键User id 所以这是一种一对多的关系 一个用户可以进行多次销售 我试图从数据库中获取它并以 JSON 格式
  • C# 和 SQL Server 中嵌套 using 的用法

    这个线程是一个延续是否有理由在 C 中使用子句检查多个内部的 null https stackoverflow com questions 2220422 is there a reason to check for null inside
  • R 中添加额外常量的累积乘法

    我是 R 新手 正在努力解决以下累积乘法与添加额外常量的组合 我希望在数据框中实现以下目标 Variable X Variable Y Variable Z X1 Y1 Y1 X1 Z1 X2 Y2 Z1 Y2 X2 Z2 X3 Y3 Z2
  • SQL查询;水平到垂直

    我遇到了涉及将水平行转换为垂直行的 SQL 查询 SQL Server 以下是我的数据 No Flag 1 Flag 2 Flag 3 A 1 2 3 B 4 1 6 转换后 该表应为 No FlagsName Flag value A F
  • 无法绑定多部分标识符

    我在 SO 上看到过类似的错误 但我找不到解决我的问题的方法 我有一个 SQL 查询 例如 SELECT DISTINCT a maxa b mahuyen a tenxa b tenhuyen ISNULL dkcd tong 0 AS
  • 在 R 中连接/匹配数据帧

    我有两个数据框 第一列有两列 x是水深 y是每个深度的温度 第二个也有两列 x也是水深 但与第一个表中的深度不同 第二栏z是盐度 我想通过以下方式连接两个表x 通过增加z到第一张桌子 我已经学会了如何使用 key 来连接表tidyr 但只有
  • MYSQL嵌套查询运行速度很慢?

    以下查询不断超时 是否有开销更少的方法来实现相同的功能 UPDATE Invoices SET ispaid 0 WHERE Invoice number IN SELECT invoice number FROM payment allo
  • PostgreSQL 中的 LATERAL JOIN 和子查询有什么区别?

    自从 PostgreSQL 推出以来 它具备了以下功能 LATERAL连接 我一直在阅读它 因为我目前为我的团队进行复杂的数据转储 其中有许多低效的子查询 使整个查询需要四分钟或更长时间 我明白那个LATERALjoins 可能可以帮助我
  • 存储过程上的 OdbcCommand - 输出参数上出现“未提供参数”错误

    我正在尝试执行存储过程 通过 ODBC 驱动程序针对 SQL Server 2005 但收到以下错误 过程或函数 GetNodeID 需要参数 ID 但未提供该参数 ID 是我的过程的 OUTPUT 参数 在存储过程中指定了一个输入 mac

随机推荐