问题描述
CREATE NONCLUSTERED INDEX [nci_wi_plan_239F85C77AF7B2990845ACFEE404C263]
ON dbo.[Plans] ([template_id] ASC,[deleted] ASC)
INCLUDE([HasWorkouts]) WITH (STATISTICS_norECOmpuTE = OFF,DROP_EXISTING = OFF,ONLINE = OFF) ON [PRIMARY]
GO
select
dp.id,dp.HasWorkouts
from dbo.Plans dp
where dp.template_id= @TemplateId and dp.deleted = 0
--((@TemplateId = 0 or @TemplateId is null) or (dp.template_id= @TemplateId)) // not working if I use this line
在上面的查询中,非聚集索引仅在我使用某个 ID 定向检查 template_id
时才有效。但有时我可能不需要检查 dp.template_id
,所以我在执行过程时将 0 or null
设置为 @TemplateId
。有没有办法检查或跳过 template_id
以及非聚集索引?
编辑:-
检查下面的屏幕截图。 if I dp.template_id= @TemplateId and dp.deleted = 0
in where 子句
如果我使用 ((@TemplateId = 0 or @TemplateId is null) or (dp.template_id= @TemplateId)) and dp.deleted = 0
解决方法
您是否尝试将@TemplateId 设置为外键?并删除template_id?我的意思是,当您可以根据一个值检查变量时,为什么还需要根据两个值来验证变量?
,添加 OPTION(RECOMPLE)
查询提示。然后,SQL Server 可以使用 constant folding 在编译期间评估这些值,并从计划中消除不需要的谓词。
OPTION(RECOMPLE)
的缺点是计划不会被重用,如果查询以高频率(即每秒多次)执行,这可能是一个问题。在这种情况下,您可以使用 IF
构造来根据值执行不同的查询,而不是单个查询。这将缓存两个计划。
如果你不想检查 template_id 就不要这样做:
IF @template_id IS NULL OR @template_id = 0
SELECT p.id dp.HasWorkouts
FROM dbo.Plans dp
WHERE dp.deleted = 0
ELSE
SELECT dp.id,dp.HasWorkouts
FROM dbo.Plans dp
WHERE dp.template_id= @TemplateId and dp.deleted = 0
这实际上是两个查询,最好的覆盖索引是;
CREATE INDEX ix_template_id_is_deleted ON dbo.Plans(template_id,is_deleted)
INCLUDE (id,HasWorkouts)
和
CREATE INDEX ix_plans_is_deleted ON dbo.Plans(is_deleted)
INCLUDE (id,hasWorkouts)
另一种方式是通过这样的唯一查询:
SELECT dp.id,dp.HasWorkouts
FROM dbo.Plans dp
WHERE
(@template_id = 0 OR @template_id IS NULL OR dp.template_id= @TemplateId)
and dp.deleted = 0
OPTION (RECOMPILE)
但这需要在每次执行时重新编译查询以提高效率。
另一个可能值得尝试的索引如下:
CREATE INDEX ix_template_not_deleted ON dbo.Plans(template_id)
INCLUDE (id,HasWorkouts)
WHERE is_deleted = 0
我认为测试这个索引是值得的,因为我认为这个索引对于两个查询来说都足够了,而且它更小。