Sql server 具有前导通配符的参数化查询的SQL Server性能

Sql server 具有前导通配符的参数化查询的SQL Server性能,sql-server,sql-server-2008,parameterized,Sql Server,Sql Server 2008,Parameterized,我有一个SQL 2008 R2数据库,其中一个表中有大约200万行,在使用参数化SQL时,我正在努力提高特定查询的性能 表中有一个字段,其中包含一个名称: [PatientsName]nvarchar NULL, 字段上还有一个简单的索引: CREATE NONCLUSTERED INDEX [IX_Study_PatientsName] ON [dbo].[Study] ( [PatientsName] ASC )WITH (PAD_INDEX = OFF, STATISTI

我有一个SQL 2008 R2数据库,其中一个表中有大约200万行,在使用参数化SQL时,我正在努力提高特定查询的性能

表中有一个字段,其中包含一个名称:


[PatientsName]nvarchar NULL,

字段上还有一个简单的索引:


CREATE NONCLUSTERED INDEX [IX_Study_PatientsName] ON [dbo].[Study] 
(
    [PatientsName] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [INDEXES]
GO
当我在management studio中执行此查询时,大约需要4秒来执行:


declare @StudyPatientsName nvarchar(64)
set @StudyPatientsName= '%Jones%'

SELECT COUNT(*) FROM Study WHERE Study.PatientsName like @StudyPatientsName
但是,当我执行此查询时:


SELECT COUNT(*) FROM Study WHERE Study.PatientsName like '%Jones%'

SELECT COUNT(*) FROM Study WHERE Study.PatientsName like @StudyPatientsName
OPTION ( OPTIMIZE FOR (@StudyPatientsName = '%Jones%'))
执行需要半秒多一点的时间

查看执行计划,没有参数化的查询使用上述索引进行索引扫描,这显然是有效的。参数化查询使用索引,但对索引执行范围搜索

部分问题在于使用前导通配符。当我删除前导通配符时,两个查询都会在几分之一秒内返回。不幸的是,我确实需要支持前导通配符

我们有一个自行开发的ORM,它在问题发生的地方执行参数化查询。这些查询是基于用户的输入完成的,因此参数化查询对于避免SQL注入攻击等事情是有意义的。我想知道是否有一种方法可以实现参数化查询功能和非参数化查询功能

我做了一些研究,寻找不同的方法向查询优化器提供提示,试图迫使优化器在每个查询上重新执行查询计划,但还没有发现任何可以提高性能的方法。我尝试了以下查询:


SELECT COUNT(*) FROM Study WHERE Study.PatientsName like '%Jones%'

SELECT COUNT(*) FROM Study WHERE Study.PatientsName like @StudyPatientsName
OPTION ( OPTIMIZE FOR (@StudyPatientsName = '%Jones%'))
这是一个有争议的解决方案,但没有什么不同


任何帮助都将不胜感激。

我认为提高性能的最佳机会是研究使用。

我很难找到文档来验证这一点,但是IIRC,COUNT(*)在MS SQL中执行完整的表扫描(而不是使用缓存值)。如果您针对一个不能为null和/或定义了索引的列运行它,我相信(同样,仍然找不到要确认的文档,因此我可能在这里偏离基准),这将更快

将查询修改为以下内容时会发生什么情况:

SELECT COUNT(id) FROM Study WHERE Study.PatientsName Like @StudyPatientsName


看起来你想强制扫描。有一个
FORCESEEK
提示,但我看不到任何类似的
FORCESCAN
提示。这应该可以做到

SELECT COUNT(*) 
FROM Study 
WHERE Study.PatientsName + '' like @StudyPatientsName
也许你可以在你的数据上试试下面的方法,看看效果如何

SELECT COUNT(*) 
FROM Study 
WHERE Study.PatientsName  like @StudyPatientsName
option (recompile)

如果一切都失败了,你可以试试

SELECT COUNT(*) FROM Study WITH(INDEX(0)) WHERE Study.PatientsName like @StudyPatientsName
也许你可以用IF来包装它

IF substring(@StudyPatientsName, 1, 1) = '%'
    SELECT COUNT(*) FROM Study WITH(INDEX(0)) WHERE Study.PatientsName like @StudyPatientsName
ELSE
    SELECT COUNT(*) FROM Study WHERE Study.PatientsName like @StudyPatientsName

编辑:正如martin指出的,对于这个特定的查询,这可能不是最好的方法,因为对现有索引的索引扫描可能更快。不过,它可能适用于类似的情况。

我尝试了查询select count(Guid)和select count(PatientsName),但在这两种情况下,查询速度都很慢。我还尝试使用相同的where子句进行select*from研究,执行时间也大致相同。他可能还可以使用索引提示强制扫描<代码>从带有(索引(IX_Study_PatientsName))的研究中选择计数(*),其中Study.PatientsName类似于@StudyPatientsName@Joe-它正在使用索引,但正在对其进行范围搜索。我认为他得到的计划是这样的:
declare@StudyPatientsName nvarchar(64)set@StudyPatientsName='%a%'从master.dbo.spt_值中选择COUNT(*),其中像@StudyPatientsName
这样的类型将空字符串添加到字段make(例如,+'')中确实有效,尽管它确实感觉像是一个黑客。添加“选项(重新编译)”不会提高性能。当我开始研究这个问题时,我真的认为使用“选项(重新编译)”可以解决这个问题。SQL一定是出了什么问题,在这种情况下它不会强制扫描索引。无论如何,我会再给它一天时间,看看是否有其他建议的答案,但看起来这应该有助于我解决这个问题。@Steve-我在
重新编译
时也得到了相当奇怪的结果。这似乎确实导致计划因不同的输入而改变,但我认为它必须以某种方式使用统计数据,我无法真正确定它是好是坏。坦率地说,如果你在做这种检查,在我看来,你的数据模型是坏的。患者姓名字段不应同时包含名字和姓氏。首先解决你的结构问题。我同意在名字和姓氏之间分割名字是解决这个问题的可行方法。不幸的是,我们还对其他几个具有相同问题的非患者姓名字段进行了查询,这些问题也必须得到解决。我同意,对前导通配符进行特殊处理是一个好主意。不过,我认为索引提示不会有任何区别。问题是它使用了正确的索引,但效率低下,似乎增加了I/O量。要重新生成,请尝试在declare@StudyPatientsName nvarchar(64)set@StudyPatientsName='%%'SELECT COUNT(*)上设置统计信息io从master.dbo.spt_值(其中@StudyPatientsName等类型)中,然后根据文档将plan和I/0与
中@StudyPatientsName等类型+''进行比较,索引(0)将强制进行表/聚集索引扫描(而不是搜索)。我怀疑你能做得比这更好。问题是,没有办法使用索引对前导通配符执行类似操作。我的答案比这更好!非聚集索引
IX_Study_PatientsName
应完全覆盖此查询,因此需要扫描该查询。这大概要窄得多,因为它只包含name列,对它的扫描将涉及更少的I/O。@Martin:你说得对。不过,我同意这是一个可行的解决问题的办法。在我的特殊情况下,使用全文是不切实际的