【问题标题】:SQL queries slow when running in sequence, but quick when running separatelySQL 查询顺序运行时慢,但单独运行时快
【发布时间】:2012-01-04 07:21:39
【问题描述】:

我有一个表,我将使用来自昂贵计算的值(使用来自不可变 XML 列的 xquery)填充该表。为了加快部署到生产的速度,我在测试服务器上预先计算了值并使用 BCP 保存到文件中。

我的脚本如下

-- Lots of other work, including modifying OtherTable

CREATE TABLE FOO (...)
GO

BULK INSERT FOO
FROM 'C:\foo.dat';
GO

-- rerun from here after the break

INSERT INTO FOO 
  (ID, TotalQuantity)
SELECT 
e.ID, 
SUM(e.Quantity) as TotalQuantity
FROM (select 
    o.ID,
    h.n.value('TotalQuantity[1]/.', 'int') as TotalQuantity
FROM dbo.OtherTable o
    CROSS APPLY XmlColumn.nodes('(item/.../salesorder/)') h(n)
WHERE o.ID NOT IN (SELECT DISTINCT ID FROM FOO)
) as E
GROUP BY e.ID

当我在 management studio 中运行脚本时,前两个语句在几秒钟内完成,但最后一个语句需要 4 小时才能完成。由于我的 foo.dat 是计算管理工作室报告 (0 row(s) affected),因此没有向 OtherTable 添加任何行。

如果我在几分钟后取消查询执行并仅选择最后一个查询并单独运行它,它将在 5 秒内完成。

值得注意的事实:

  • OtherTable 包含 200k 行,XmlColumn 中的数据非常大,总表大小 ~3GB
  • FOO 表有 130 万行

什么可能会有所作为?
Management Studio 已关闭隐式事务。据我所知,每条语句都会在自己的事务中运行。

更新:
如果我首先选择并运行脚本直到-- rerun from here after the break,然后选择并运行最后一个查询,它仍然很慢,直到我取消执行并重试。这至少排除了与脚本中的先前代码“一起”运行的任何影响,并归结为相同的查询在第一次执行时很慢,在第二次执行时很快(在所有其他条件相同的情况下运行)。

【问题讨论】:

  • 你能看出执行计划有什么不同吗?由于最后一条语句需要 4 小时,您可以查看估计计划而不是实际计划(至少在开始时)。
  • “如果我在几分钟后取消查询执行并只选择最后一个查询并单独运行它,它将在 5 秒内完成。” - 您是自行运行选择,将结果插入到空的 foo 中,还是将结果插入到已填充的 foo 中? foo 是否主要从 BCP 进程或从 OtherTable 的插入中获得 130 万行?
  • @MarkBannister,我正在运行选择并填充表格。我只是从按下取消的位置继续执行相同的脚本。所有 130 万行都来自批量插入。 (这就是(0 row(s) affected) 所表示的)。

标签: sql sql-server


【解决方案1】:

【讨论】:

  • 我使用fulltablescan.com/index.php?/archives/… 来获取运行缓慢的查询的执行计划,但据我所知,它在我第二次运行查询时使用相同的执行计划。不幸的是,除非我等待 4 小时,否则我无法获得每个部分的执行计数(我对 xpath 评估的数量感兴趣)。我会尝试让查询在一夜之间运行以获得完整的执行计划。
  • 此外,为什么在 Management Studio 的两个相同调用之间执行计划会发生变化,而两者之间没有其他活动?
  • why would the execution plan change between: 统计数据
【解决方案2】:

这可能与新创建的Foo 表上的统计信息完全错误有关吗?如果 SQL Server 在第一次运行查询时自动更新统计信息,则第二次运行将根据最新统计信息创建其执行计划。

如果您在批量插入后立即检查统计信息(使用STATS_DATE 函数),然后在取消长时间运行的查询后再次检查,该怎么办?即使查询已取消,统计信息是否已更新?

在这种情况下,Foo 上的 UPDATE STATISTICS 在批量插入之后会有所帮助。

【讨论】:

  • 查询执行后,FOO 表的 PK 的 stats-date 为空。听起来像是一个可能的解决办法。但是,我设法通过将查询重写为LEFT OUTER JOIN 来获得合理的执行时间。
【解决方案3】:

不知道为什么它有帮助,但我将最后一个查询改写为left outer join,然后突然执行下降到 15 毫秒。

INSERT INTO FOO 
  (ID, TotalQuantity)
SELECT 
e.ID, 
SUM(e.Quantity) as TotalQuantity
FROM (select 
    o.ID,
    h.n.value('TotalQuantity[1]/.', 'int') as TotalQuantity
FROM dbo.OtherTable o
INNER JOIN FOO f ON o.ID = f.ID
    CROSS APPLY o.XmlColumn.nodes('(item/.../salesorder/)') h(n)
WHERE f.ID = null
) as E
GROUP BY e.ID

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-21
    相关资源
    最近更新 更多