【问题标题】:TSQL while loop vs. mergeTSQL while 循环与合并
【发布时间】:2013-05-09 07:03:50
【问题描述】:

总的来说,我对 SQL 和数据库还很陌生。我正在使用 SQL Server 2008 Mgmt Studio。

我读到使用基于集合的操作比 RBAR 更好(今天才知道!)。

很快,我将向您展示两个等效的(我认为)查询,我想看看哪个更 高效。

第一次尝试:

DECLARE @persID int
DECLARE @mag    float
DECLARE @temp TABLE (pID int PRIMARY KEY)

INSERT INTO @temp
SELECT persID FROM Person

WHILE (SELECT COUNT(pID) FROM @temp) > 0
BEGIN
   SELECT TOP 1 @persID = pID FROM @temp    
   SELECT @mag = SQRT(SUM(value*value)) FROM PersonWord 
      WHERE PersonWord.persID = @persID

   UPDATE Person
   SET magnitude = @mag
      WHERE persID  = @persID
   DELETE @temp WHERE pID = @persID
END

第二次尝试:

DECLARE @temp TABLE (pID int PRIMARY KEY, mag float)

INSERT INTO @temp
   SELECT persID, SQRT(SUM(value*value)) FROM PersonWord
      GROUP BY persID

MERGE INTO Person AS p
USING @temp AS t
   ON p.persID = t.pID
WHEN MATCHED
THEN UPDATE
   SET magnitude = t.mag

这些在运行时保存为存储过程和估计的执行计划:

exec FirstAttempt
exec SecondAttempt

第一次尝试显示 32% 的批次,第二次尝试显示 68% 的批次

PersonWord 表包含大约 4100 万条记录...Person 表包含 大约 170,000

欢迎提出任何想法/建议。感谢您抽出宝贵的时间,我知道新手问题是多么令人沮丧(用于在 Yahoo 上做数学帮助)。

编辑::

在拥有约 130 万条记录的 PersonWord 和拥有约 3000 条记录的 PersonWord 上运行这些...合并的版本大约需要 1.3 秒才能执行。带有 while 循环的版本用时 6 分钟,只完成了约 15% 的工作。

对于这种事情,基于集合而不是 RBAR!

【问题讨论】:

  • 您已经看到了预估的执行计划,可以得到实际的执行计划。您有适合 StackOverflow 的问题吗?
  • 在 Sql Server Mgt Studio 中,右键单击查询并选择include client statistics,然后将每个查询运行六次左右。您将在输出中获得一个带有时间信息的额外选项卡。 “批次百分比”对我来说不是一个有用的指标。​​
  • @HABO 是的,对于那些了解 sql 来龙去脉的人来说,合并是否比 while 循环更好?我已经在这里待了 2 天,试图找到答案却得到了相互矛盾的结果
  • @DanPichelman 感谢 Dan,现在在大大减少的数据集(分别为 1 百万和 3,000)上运行它
  • 1) 确保您在 Person.persId 上有一个索引 - 索引是加快数据库速度的第一方法; 2)在这种情况下,您不需要“合并”,因为您只做一件事(更新会做同样的事情); 3)如果您的@temp 表有很多记录,请使用#temp 表而不是表变量(当有很多记录时,临时表要快得多)-您还可以向#temp 表添加索引; 4)你自己做研究很好,但如果你发现基于集合的方法最终变慢了,那么你做错了什么;)

标签: sql-server tsql merge while-loop


【解决方案1】:

永远不要为该数量的记录使用表变量。它们适用于小型数据集。请改用临时表并将其编入索引。此外,我个人会寻找一种方法来限制您正在更新的记录数量。我不确定您为什么要使用合并,因为这是一个简单的更新。下面的代码应该可以工作。

Update P
set   SET magnitude = t.mag
from Person AS p
join  #temp AS t
   ON p.persID = t.pID
WHERE magnitude <> t.mag

根据您执行此操作的频率,我会尝试存储此计算,因此它只需要为每条记录发生一次(如果值发生变化,请使用 rtigger 保持更新): SQRT(SUM(值*值))

顺便说一句,对于任何数学计算来说,浮动都是一种不好的做法,因为它不准确,所以会引入舍入误差。

【讨论】:

  • 感谢 HLGEM,我改为使用临时表。这个和其他类似的程序不会经常使用,可能一年 4 次。此外,不需要精确的 mag 值,即使在 10^-6 处舍入也不会影响 mag 值的用途。我使用 Merge 是因为我读到它基本上是 Update...From,但我喜欢你的解决方案,因为我更清楚地看到发生了什么。
  • MERGE 是“Upsert”的 TSQL 词。又名,有“当你有匹配时做什么”和“当你没有匹配时做什么”(在源或目标中)。如果您知道您将有“匹配”,请使用更新(如已建议的那样)。
  • @granadaCoder 啊,我明白了,当没有匹配项时,什么都没有发生...进行更新,它就像一场梦
  • 不需要触发器来保持这样的字段最新:只需使用计算列。如果您希望经常使用该字段并且不介意占用更多空间,请标记它PERSISTED;如果您希望不经常使用它并且不介意一些 CPU 周期,请让它保持动态。
猜你喜欢
  • 1970-01-01
  • 2012-08-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-29
  • 2023-03-04
  • 2011-07-05
相关资源
最近更新 更多