当我尝试将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。
查询计划:
现在,让我们将查询更改为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
查询)。
让我们看一下查询计划......
现在让我们尝试使用原始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秒钟和计划..
INSERT INTO
需要45多秒时。OPTION (RECOMPILE)
,则相同。sp_executesql
而不仅仅是原始的sql语句?因为我们有一个动态WHERE / AND
语句的堆,这些语句开始让事情变得困难/不那么好。技术..
- Sql Server 2012
答案 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
调用1>}内的EXEC sp_executesql
语句,则可能能够解决此性能下降问题。通过这样做,SELECT
语句的结果可以再次直接流式传输到INSERT
语句。
参考文献:这是一篇有趣的文章,讨论了您所面临的问题:The Hidden Costs of INSERT EXEC。
暂时不说:如果您所做的只是在之后插入数据,那么您不需要ORDER BY
子句。
答案 1 :(得分:0)
评论时间有点长。
您尚未显示整个执行计划,但“索引扫描”会显示正在发生的事情。正在使用索引按顺序检索数据。这意味着没有额外的排序步骤,查询可以立即开始返回数据。你回来时会看到结果。
证据?如果有数百万行,则返回结果时应该有一些延迟。如果必须对数据进行排序,则必须读取行然后进行排序。
另一方面,insert into
必须等到 all 生成数据,因为它必须将所有结果插入表中。在读完最后一行之前它不会返回。
编辑:
我注意到另一个问题。主索引希望数据按主键排序。您正在按其他值对其进行排序。毫无疑问,额外种类的数据需要额外的时间。然而,因数10似乎有点极端。
答案 2 :(得分:0)
遗憾的是,我此时没有详细说明的机器,但我认为区别在于使用主键声明临时表。在第二个查询执行计划中,您可以看到聚集索引插入占用了大部分时间。
使用没有主键的临时表应该可以达到相同的性能。