与INSERT INTO一起使用时,EXEC sp_executesql真的很慢:(

时间:2016-08-10 02:06:03

标签: sql sql-server performance database-performance sp-executesql

当我尝试将sp_executesql的一些结果插入到变量表中时,我的表现非常糟糕。

首先,查询就像一个简单的选择。

EXEC sp_executesql N'SELECT      [a].[ListingId]
FROM        [dbo].[Listings] [a] LEFT OUTER JOIN 
            [dbo].[AgencyCompany] [b] ON [a].[AgencyCompanyId] = [b].[AgencyCompanyId]
ORDER BY    UpdatedOn DESC'

这会在几秒钟后运行,因为有几百万个结果从云中的数据库传递到我的localhost机器。所以这完全是kewl。

查询计划:

enter image description here

现在,让我们将查询更改为INSERT INTO结果...

DECLARE @ListingIds TABLE (ListingId INTEGER PRIMARY KEY)
INSERT INTO @ListingIds
EXEC sp_executesql N'SELECT      [a].[ListingId]
FROM        [dbo].[Listings] [a] LEFT OUTER JOIN 
            [dbo].[AgencyCompany] [b] ON [a].[AgencyCompanyId] = [b].[AgencyCompanyId]
ORDER BY    UpdatedOn DESC'

将结果返回到我的localhost计算机大约需要45秒。 相同查询(同样,SELECT查询)。

让我们看一下查询计划......

enter image description here

现在让我们尝试使用原始sql ...

DECLARE @ListingIds TABLE (ListingId INTEGER PRIMARY KEY)
INSERT INTO @ListingIds
SELECT      [a].[ListingId]
FROM        [dbo].[Listings] [a] LEFT OUTER JOIN 
            [dbo].[AgencyCompany] [b] ON [a].[AgencyCompanyId] = [b].[AgencyCompanyId]
ORDER BY    UpdatedOn DESC

运行4秒钟和计划..

enter image description here

  • 因此,当我正常运行查询时,它可以正常运行。
  • 当我INSERT INTO需要45多秒时。
  • 同时我还有params。
  • 如果我使用OPTION (RECOMPILE),则相同。
  • 为什么使用sp_executesql而不仅仅是原始的sql语句?因为我们有一个动态WHERE / AND语句的,这些语句开始让事情变得困难/不那么好。

技术..
- Sql Server 2012

enter image description here

3 个答案:

答案 0 :(得分:6)

两个陈述之间的区别在于 raw 版本:

DECLARE @ListingIds TABLE (ListingId INTEGER PRIMARY KEY)
INSERT INTO @ListingIds
SELECT      [a].[ListingId]
FROM        [dbo].[Listings] [a] LEFT OUTER JOIN 
            [dbo].[AgencyCompany] [b] ON [a].[AgencyCompanyId] = [b].[AgencyCompanyId]
ORDER BY    UpdatedOn DESC

... select的结果会直接流入insert语句。

但在动态版中:

DECLARE @ListingIds TABLE (ListingId INTEGER PRIMARY KEY)
INSERT INTO @ListingIds
EXEC sp_executesql N'SELECT      [a].[ListingId]
FROM        [dbo].[Listings] [a] LEFT OUTER JOIN 
            [dbo].[AgencyCompany] [b] ON [a].[AgencyCompanyId] = [b].[AgencyCompanyId]
ORDER BY    UpdatedOn DESC'

... EXEC sp_executesql的结果累积到一个名为 参数表 的临时表中(您可以看到额外的步骤)你的执行计划)。只有这个临时表填充了数百万行后,insert语句实际上才开始读取数据。这要慢得多。

如果您能够以某种方式重构代码以推送INSERT调用}内的EXEC sp_executesql语句,则可能能够解决此性能下降问题。通过这样做,SELECT语句的结果可以再次直接流式传输到INSERT语句。

参考文献:这是一篇有趣的文章,讨论了您所面临的问题:The Hidden Costs of INSERT EXEC

暂时不说:如果您所做的只是在之后插入数据,那么您不需要ORDER BY子句。

答案 1 :(得分:0)

评论时间有点长。

您尚未显示整个执行计划,但“索引扫描”会显示正在发生的事情。正在使用索引按顺序检索数据。这意味着没有额外的排序步骤,查询可以立即开始返回数据。你回来时会看到结果。

证据?如果有数百万行,则返回结果时应该有一些延迟。如果必须对数据进行排序,则必须读取行然后进行排序。

另一方面,insert into必须等到 all 生成数据,因为它必须将所有结果插入表中。在读完最后一行之前它不会返回。

编辑:

我注意到另一个问题。主索引希望数据按主键排序。您正在按其他值对其进行排序。毫无疑问,额外种类的数据需要额外的时间。然而,因数10似乎有点极端。

答案 2 :(得分:0)

遗憾的是,我此时没有详细说明的机器,但我认为区别在于使用主键声明临时表。在第二个查询执行计划中,您可以看到聚集索引插入占用了大部分时间。

使用没有主键的临时表应该可以达到相同的性能。