【问题标题】:Execution Plan shows a sort - but can't figure a way around because of query执行计划显示排序 - 但由于查询无法解决
【发布时间】:2015-07-10 16:49:22
【问题描述】:

这是我的查询——最后一个查询是让我感到痛苦的原因: address.postcode 字段是一个varchar(14),可以看到用户发送的输入格式。

DECLARE @ZipCode NVARCHAR(MAX) = ('06409;06471;11763;06443;06371;11949;11946;11742')

IF OBJECT_ID('tempdb..#ZipCodes') IS NOT NULL DROP TABLE #ZipCodes;

CREATE TABLE #ZipCodes (
Zipcode NVARCHAR(6)
)

INSERT INTO #ZipCodes ( Zipcode )
SELECT zip.Token + '%'
FROM DMS.fn_SplitList(@ZipCode, ';')  zip

CREATE NONCLUSTERED INDEX [idx_Zip] ON #ZipCodes (Zipcode)

IF OBJECT_ID('tempdb..#ZipCodesConstituents') IS NOT NULL DROP TABLE #ZipCodesConstituents;

CREATE TABLE #ZipCodesConstituents (
ConstituentID UNIQUEIDENTIFIER
, PostCode NVARCHAR(12)
)
CREATE NONCLUSTERED INDEX [idx_ZipCodesConstituents] ON #ZipCodesConstituents (ConstituentID, PostCode)

INSERT INTO #ZipCodesConstituents ( ConstituentID, PostCode )
SELECT a.CONSTITUENTID
, a.POSTCODE
FROM #ZipCodes zip
JOIN DMS.address  a  
    ON a.POSTCODE LIKE zip.Zipcode  
where a.ISPRIMARY = 1

我正在尝试附上执行计划——但没有任何运气... 基本上这部分代码的 Est Cost 为 61.9% 排序为 61.5%

【问题讨论】:

  • 问题是什么?
  • 我想提高性能,但排序导致供过于求。想知道如何摆脱排序或其他优化方式的任何想法。
  • 为什么要在插入之前创建索引?我不知道没有执行计划的排序,但是有索引会减慢每次插入的速度,因为系统必须为插入的每一行更新索引。这实际上可能是导致排序的原因,因为 sql 正在寻找一种方法来最小化索引的影响。
  • 哇,就是这么简单。排序不见了。谢谢@Randall Matthews!
  • 哦,但现在我回到了原来的问题,我对 DMS.ADDRESS 表进行了巨大的索引扫描并提取了 150,485,448 行,但表插入只有 10,313 行。出于某种原因,尽早添加该索引可以防止这种情况发生....基本上,该计划显示使用索引 idx_zip 对#ZipCodes 进行索引扫描 - 并使用索引对 ADDRESS 进行索引扫描,但提取地址中的所有行 - 然后是嵌套循环内连接,然后插入到表中#ZipCodesConstituents --- 有什么想法可以摆脱我的 FAT 箭头?

标签: sql sorting execution


【解决方案1】:

我试图评估该行为,但在我的测试中,我无法在最后一个 INSERT 中强制使用排序运算符。但我看到的是以下两个问题。

  1. 您创建索引,然后插入到表中。这可能有害。并非在所有情况下,但它可能会强制在您的查询中进行排序,因为 SQL Server 会尝试在插入期间确保正确的顺序以帮助索引重组。
  2. 您使用UNIQUEIDENTIFIER 作为您的主键。这在某种程度上可能很有用,但我认为在您的情况下,一个简单的 IDENTITY(1,1) 列就足够了,不是吗?索引中的 UNIQUEIDENTIFIER 将严重强制产生碎片。以这种方式解决它可能不是最好的主意。

我使用 100.000 行的测试集测试了这两种变体。 以下是我衡量执行成本的结果:

  • 在所有情况下,如果在#ZipCodesConstituentsINSERT 之后创建索引,则成本会显着降低。
  • 使用INDENTITY 而不是UNIQUEIDENTIFIER 将额外提高性能。
  • 如果您更频繁地运行此类查询,最好在地址表中添加索引。

以下是成本点 (cp) 的测量值 - 越低越好:

  • UniqueIdentifier + 之前的索引:Insert 为 20 cp。
  • UniqueIdentifier + 插入后的索引:Insert 为 8 cp + 索引为 7(SORT 出现的位置)。
  • Identity + 之前的索引:Insert 的 18 cp
  • Identity + 之后的索引:Insert 为 7 cp + 索引为 6
  • Identity + 后索引 + Address 上的索引:Insert 为 3@ + 索引为 6

获胜者是:Identity 列 + 索引,也可能是您的 Address 上的索引。

我用来提升这两个语句(UniqueIdentifierIdentity)的索引是这个:

CREATE NONCLUSTERED INDEX [NCI_Adress_IsPrimary_Postcode]
ON Address ([IsPrimary],[PostCode])
INCLUDE ([Constituentid])

在我的测试用例中,构建它需要 13 cp。如果您只使用一次,它将无济于事!如果您经常使用此语句,甚至有时每天/每周使用一次,它可能对您有用。

希望这能解决您的问题。

【讨论】:

  • 谢谢@Ionic。我必须保留唯一标识符才能加入另一个表,但我认为通过将索引放在插入之后并添加地址表索引,我将获得我可以预期的最佳性能——因为我必须保留唯一标识符。
  • 是的,identityuniqueidentifier 之间的区别在这个用例中是微不足道的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-05
  • 2022-01-26
  • 2012-09-02
  • 1970-01-01
  • 1970-01-01
  • 2014-06-29
  • 2020-11-16
相关资源
最近更新 更多