避免where子句中的非sargable参数的解决方案

woo*_*gie 1 sql t-sql sql-server sql-server-2008

code_list这个查询的CTE中,我有一个行构造函数,最终将获取任意数量的参数.该柱icdpatient_codesCTE是五位数字标识符是最具描述,这三个位代码,该行构造函数.该表icd_patient有1亿行,所以为了性能,我想在我做任何进一步的工作之前在这个表上存档行.我有

;with code_list(code_list)
as
(
    select  x.code_list
      from (values ('70700'),('25002')) as x(code_list)
),patient_codes
as
(

select distinct  icd,pat_id,id
    from icd_patient
    where icd in (select icd from code_list)
)
select distinct pat_id from patient_codes
Run Code Online (Sandbox Code Playgroud)

但问题是,在icd_patient表中,所有icd列都是五位数且更具描述性.如果我查看此查询的执行计划,它会非常精简.如果我做

;with code_list(code_list)
as
(
    select  x.code_list
      from (values ('70700'),('25002')) as x(code_list)
),patient_codes
as
(
    select substring(icd,1,3) as icd,pat_id
      from icd_patient2
      where substring(icd,1,3) in (select * from code_list)
)
select * from patient_codes
Run Code Online (Sandbox Code Playgroud)

如果由于where子句中的子字符串表达式,课程会对性能产生很大影响.有类似的东西like in存在,所以我可以利用我的索引?

icd_patient上的索引 CREATE NONCLUSTERED INDEX [ix_icd_patient] ON [dbo].[icd_patient2] ( [pat_id] ASC ) INCLUDE ( [id],

Aar*_*and 5

这个更简单的查询应该比现有查询更好(或者,最坏,相同).

select pat_id
    FROM dbo.icd_patient
    where icd LIKE '707%'
       OR icd LIKE '250%'
GROUP BY pat_id;
Run Code Online (Sandbox Code Playgroud)

请注意,只有在此列上实际存在索引时,sargability才有意义.

另一种选择(因为OR有时可以使优化器适合):

SELECT pat_id FROM 
(
  SELECT pat_id
    FROM dbo.icd_patient
    WHERE icd LIKE '707%'
  UNION ALL
  SELECT pat_id
    FROM dbo.icd_patient
    WHERE icd LIKE '250%'
) AS x
GROUP BY pat_id;
Run Code Online (Sandbox Code Playgroud)

为了使这个可扩展超出少数OR条件,我将使用表值参数(TVP).

CREATE TYPE dbo.StringPatterns AS TABLE(s VARCHAR(3) PRIMARY KEY);
Run Code Online (Sandbox Code Playgroud)

然后你的存储过程可以说:

CREATE PROCEDURE dbo.whatever
  @sp dbo.StringPatterns READONLY
AS
BEGIN
  SET NOCOUNT ON;

  SELECT p.pat_id
    FROM dbo.icd_patient AS p
    INNER JOIN @sp AS sp
    ON p.pat_id LIKE sp.s + '%'
  GROUP BY p.pat_id;
END
Run Code Online (Sandbox Code Playgroud)

然后,您可以从DataTableC#中的一个或其他集合中传入一组三字符子串.以T-SQL为例:

DECLARE @p dbo.StringPatterns;
INSERT @p VALUES('707'),('250');
EXEC dbo.whatever @sp = @p;
Run Code Online (Sandbox Code Playgroud)