PATIENTS 表中的 SURNAME 上有一个索引,我们运行以下查询但该索引没有被使用。
DECLARE @SURNAME VARCHAR(30) = 'test'
SELECT SURNAME
FROM PATIENTS AS PAT
WHERE (PAT.SURNAME LIKE @SURNAME + '%' OR @SURNAME IS NULL)
Run Code Online (Sandbox Code Playgroud)
这似乎是因为我们检查变量是否NULL就像我删除了它然后我得到了一个常量扫描、计算标量、索引查找和嵌套循环。
我们需要检查参数是否NULL为我们然后在 where 子句中忽略它。
有没有办法做到这一点并让 SQL 使用索引?
Mar*_*ith 10
为什么此查询不使用 SURNAME 上存在的索引?
因为它必须编译一个计划,该计划在@SURNAME为 NULL 和不为 NULL的情况下都能正常工作。
原则上,SQL Server 创建这样一个既适用于两种情况又仍使用索引的计划并非不可能。该动态寻求机制已经与该案件科佩斯@SURNAME包含通配符领先,因此该指数的整体不是空部分需要中定位-但是你的要求是目前不能实现的东西。
以上仅在索引覆盖查询的情况下才有用(如您的示例)。如果索引是非覆盖的,则不需要对整个索引进行范围查找,然后需要查找整个表。
有没有办法让 SQL Server 使用索引?
如果PAT.SURNAME不可为空,你可以做
DECLARE @SURNAME VARCHAR(30) = 'test'
SELECT SURNAME
FROM PATIENTS AS PAT
WHERE ( PAT.SURNAME LIKE ISNULL(@SURNAME, '') + '%' )
Run Code Online (Sandbox Code Playgroud)
或者,您可以每次都请求新的计划编译,同时考虑到执行的参数化值,使用
DECLARE @SURNAME VARCHAR(30) = 'test'
SELECT SURNAME
FROM PATIENTS AS PAT
WHERE ( PAT.SURNAME LIKE @SURNAME + '%'
OR @SURNAME IS NULL )
OPTION (RECOMPILE)
Run Code Online (Sandbox Code Playgroud)
(只要您使用的是支持参数嵌入行为的相当新的版本。)
或者你可以做
DECLARE @SURNAME VARCHAR(30) = 'test'
SELECT SURNAME
FROM PATIENTS AS PAT
WHERE ( PAT.SURNAME LIKE @SURNAME + '%'
AND @SURNAME IS NOT NULL )
UNION ALL
SELECT SURNAME
FROM PATIENTS AS PAT
WHERE ( @SURNAME IS NULL )
Run Code Online (Sandbox Code Playgroud)
上面的计划将显示两个表引用,但希望它们都在带有启动谓词的过滤器下,这样只有相关的分支才会实际执行。
最后一种选择是只使用两个完全独立的查询,然后选择具有程序逻辑的正确查询。
我建议查看T?SQL中的动态搜索条件作为有关该主题的信息性文章。