SQL中的最后一个UNION忽略现有的INDEX

时间:2013-12-05 19:25:52

标签: sql stored-procedures indexing query-optimization union

这是一个我已经苦苦挣扎了几天的精致问题。

我有一个静态表,我创建并构建索引,然后我创建一个存储过程来运行。我的问题很奇怪,我会尽力解释。

我运行相同的脚本来创建和执行194个数据库......其中绝大多数运行得非常快......但是在少数几个数据库中,它们的运行速度非常慢。

我们很清楚这里是INDEX:

CREATE UNIQUE CLUSTERED INDEX IX_ID
ON DC_DuplicateMatch(ID)
go
CREATE INDEX IX_LastName_FirstName
ON DC_DuplicateMatch(ID, LastName, FirstName)
GO
CREATE INDEX IX_LastName_PostalCode
ON DC_DuplicateMatch(ID, LastName, PostalCode)
GO
CREATE INDEX IX_LastName_YearBorn 
ON DC_DuplicateMatch(ID, LastName, YearBorn)
GO
CREATE INDEX IX_FirstName_PostalCode 
ON DC_DuplicateMatch(ID, FirstName, PostalCode)
GO
CREATE INDEX IX_FirstName_YearBorn 
ON DC_DuplicateMatch(ID, FirstName, YearBorn)
GO
CREATE INDEX IX_PostalCode_YearBorn 
ON DC_DuplicateMatch(ID, PostalCode, YearBorn)
GO

以下是存储过程:

CREATE PROC dbo.DC_GetPotentialDuplicates
    @ID    int,   
    @FirstName  varchar(30),   
    @LastName   varchar(30),   
    @PostalCode varchar(10),
    @YearBorn   varchar(4)    
AS
SELECT *  
FROM    DC_DuplicateMatch WITH(INDEX(IX_LastName_FirstName))
WHERE   ID > @ID AND 
        (LastName   = @LastName   AND 
        FirstName  = @FirstName)
UNION            
SELECT  *
FROM    DC_DuplicateMatch WITH(INDEX(IX_LastName_PostalCode))
WHERE   ID > @ID AND 
        (LastName   = @LastName   AND 
        PostalCode = @PostalCode)
UNION            
SELECT  *
FROM    DC_DuplicateMatch WITH(INDEX(IX_LastName_YearBorn))
WHERE   ID > @ID AND 
        (LastName   = @LastName   AND 
        YearBorn   = @YearBorn)
UNION
SELECT  *
FROM    DC_DuplicateMatch WITH(INDEX(IX_FirstName_PostalCode))
WHERE   ID > @ID AND 
        (FirstName  = @FirstName  AND 
        PostalCode = @PostalCode)
UNION            
SELECT  *
FROM    DC_DuplicateMatch WITH(INDEX(IX_FirstName_YearBorn))
WHERE   ID > @ID AND 
        (FirstName  = @FirstName  AND 
        YearBorn   = @YearBorn)
UNION
SELECT  *
FROM    DC_DuplicateMatch WITH(INDEX(IX_PostalCode_YearBorn))
WHERE   ID > @ID AND 
        (PostalCode = @PostalCode AND 
        YearBorn   = @YearBorn)
GO

表定义

ID          int no  4  10   0   
FirstName   varchar no  30              
LastName    varchar no  30          
PostalCode  char    no  10          
YearBorn    varchar no  4           

这个proc在较大的表上运行得更快...较小的表“偶尔”运行得更慢。速度范围从4,000记录/秒“快速”下降到70记录/秒“慢”。

问题是,如果我在某个时刻将空白填充记录添加到目标表中,而没有任何其他更改,则速度从70向上增加到接近4,000标记。就好像查询计划没有根据表中的记录数正确构建。

运行数据库引擎调优器和性能监视器后,我发现问题是SQL忽略了最后一个UNION上的INDEX,并对某些查询进行了表扫描。并且执行计划明确地说我需要创建我已经在表上的确切索引(因此INDEX Hints)。

所以我删除了第6个选择UNION

问题仍然存在;然而这一次它抱怨说缺失的指数再次出现在工会的最后一个表格上(这是上面列出的第5个表格,当有6个选择的UNIONS时没有问题)。

对于为什么会发生这种情况或者我可以采取哪些措施来避免它? (除了添加虚拟空白记录以增加表格大小或创建假的第7个最终联盟......这两者都会提高性能)。

2 个答案:

答案 0 :(得分:1)

现有索引不是那么好,因为ID是前导列。这使得他们在这种情况下无法实现。这是一个更好的设置:

CREATE INDEX IX_LastName_FirstName
ON DC_DuplicateMatch(LastName, FirstName, ID) INCLUDE (PostalCode, YearBorn)
GO
CREATE INDEX IX_LastName_PostalCode
ON DC_DuplicateMatch(LastName, PostalCode, ID) INCLUDE (FirstName, YearBorn)
GO
CREATE INDEX IX_LastName_YearBorn 
ON DC_DuplicateMatch(LastName, YearBorn, ID) INCLUDE (FirstName, PostalCode)
GO
CREATE INDEX IX_FirstName_PostalCode 
ON DC_DuplicateMatch(FirstName, PostalCode, ID) INCLUDE (LastName, YearBorn)
GO
CREATE INDEX IX_FirstName_YearBorn 
ON DC_DuplicateMatch(FirstName, YearBorn, ID) INCLUDE (LastName, PostalCode)
GO
CREATE INDEX IX_PostalCode_YearBorn 
ON DC_DuplicateMatch(PostalCode, YearBorn, ID) INCLUDE (LastName, FirstName)
GO

这些非常适合此查询。

enter image description here

每个查询都有6个索引。我们需要采取一些严厉的技巧来打败这个。

答案 1 :(得分:0)

根据我的经验,大多数情况下,查询规划器不使用适当的索引,因为需要重新运行update statistics以使优化器正常工作。这似乎表明您的查询计划只对某些值有害。

由于您提供索引提示,但这似乎不太可能 - 通常提示会强制规划人员使用该索引,但这可能取决于您的供应商。