我有一个静态表,我创建并构建索引,然后我创建一个存储过程来运行。我的问题很奇怪,我会尽力解释。
我运行相同的脚本来创建和执行194个数据库......其中绝大多数运行得非常快......但是在少数几个数据库中,它们的运行速度非常慢。
CREATE PROC dbo.DC_GetPotentialDuplicates
@ID int,
@FirstName varchar(30),
@LastName varchar(30),
@PostalCode varchar(10),
@YearBorn varchar(4)
AS
SELECT *
FROM DC_DuplicateMatch
WHERE (ID > @ID) AND
(
(LastName = @LastName AND FirstName = @FirstName) OR
(LastName = @LastName AND PostalCode = @PostalCode) OR
(LastName = @LastName AND YearBorn = @YearBorn) OR
(FirstName = @FirstName AND PostalCode = @PostalCode) OR
(FirstName = @FirstName AND YearBorn = @YearBorn) OR
(PostalCode = @PostalCode AND YearBorn = @YearBorn)
)
OPTION (RECOMPILE)
GO
这个proc在较大的表上运行得更快...较小的表“偶尔”运行得更慢。速度范围从4,000记录/秒“快速”下降到70记录/秒“慢”。
问题是,如果我在某个时刻将空白填充记录添加到目标表而没有任何其他更改,则速度从70向上增加到接近4,000标记。就好像查询计划没有根据表中的记录数正确构建。
为了避免添加空白记录以获得速度,我重写了存储过程以使用UNIONS而不是OR(生成相同的输出结果)
CREATE PROC dbo.DC_GetPotentialDuplicates
@VoterID int,
@FirstName varchar(30),
@LastName varchar(30),
@PostalCode varchar(10),
@YearBorn varchar(4)
AS
SELECT *
FROM DC_DuplicateMatch
WHERE ID > @ID AND
(LastName = @LastName AND FirstName = @FirstName)
UNION
SELECT *
FROM DC_DuplicateMatch
WHERE ID > @ID AND
(LastName = @LastName AND PostalCode = @PostalCode)
UNION
SELECT *
FROM DC_DuplicateMatch
WHERE ID > @ID AND
(LastName = @LastName AND YearBorn = @YearBorn)
UNION
SELECT *
FROM DC_DuplicateMatch
WHERE ID > @ID AND
(FirstName = @FirstName AND PostalCode = @PostalCode)
UNION
SELECT *
FROM DC_DuplicateMatch
WHERE ID > @ID AND
(FirstName = @FirstName AND YearBorn = @YearBorn)
UNION
SELECT *
FROM DC_DuplicateMatch
WHERE ID > @ID AND
(PostalCode = @PostalCode AND YearBorn = @YearBorn)
GO
现在这个存储过程在所有大型表上的运行速度一样快......并且在大约1/2个较小的有问题的表上提高了速度......但只能让我对剩余部分进行微小的改进。如果我用那些较小的表上的空白记录增加记录计数,性能会提高。
在运行数据库引擎调优器和性能监视器之后,无论强制计划还是我尝试的任何优化,无论是强制计划还是任何优化,任何触发较小问题表的proc都会导致表扫描。
关于我应该考虑解决这个问题的任何建议?
~~~~修订~~~~~
我已经大大缩小了这个问题,它与缺少的缺失索引有关。
我刚刚发现,当我重新订购UNION时,列表中的最后一个即使它在那里也无法使用INDEX。**
在上面的代码示例中,我实际上交换了第一个和最后一个联合,现在存储的proc对LastName和FirstName进行了表扫描,并且Profiler抱怨INDEX丢失了。
~~~~~~~~~
我创建了一个新问题,可以更好地定义问题,并包含一个位于此处的解决方案
答案 0 :(得分:1)
不是检查每个排列,而是计算重复项:
SELECT *
FROM DC_DuplicateMatch
WHERE (ID > @ID) AND
(
CASE [LastName] WHEN @LastName THEN 1 ELSE 0 END +
CASE [FirstName] WHEN @FirstName THEN 1 ELSE 0 END +
CASE [PostalCode] WHEN @PostalCode THEN 1 ELSE 0 END +
CASE [YearBorn] WHEN @YearBorn THEN 1 ELSE 0 END
) >= 2