对于下面的查询,我试图弄清楚为什么 SQL Server 在 sysjobhistory 表上建议的索引(也是导致查找的索引)是在 Job_Id 列上创建的,其中包括其他列:
Job_Id 包括(Run_date、Run_time、Instance_id)
据我了解,where 子句中的列应该出现在索引键中,以便可以进行搜索。Run_date 和 Run_time 出现在下面查询的 where 子句中,那么我们为什么可以将它们包含在索引中而不是将它们作为键列呢?
查询:
SELECT TOP 10000 run_date
FROM msdb.dbo.sysjobhistory sh
WHERE EXISTS (SELECT 1 FROM msdb.dbo.sysjobs sj WHERE sh.job_id = sj.job_id
AND EXISTS (SELECT 1 FROM msdb.dbo.sysjobschedules sjs WHERE sjs.job_id = sj.job_id
AND EXISTS (SELECT 1 FROM msdb.dbo.sysschedules ss WHERE ss.schedule_id = sjs.schedule_id
AND ss.freq_subday_type = 2
AND ss.freq_subday_interval = 10 )))
AND CAST(CAST([run_date] AS CHAR(8)) + ' ' + STUFF(STUFF(RIGHT('000000' + CAST([run_time] AS VARCHAR(6)), 6), 3, 0, ':'), 6, 0, ':') AS DATETIME) < dateadd(hh,-12,getdate())
注意:我没有使用联接,因为该查询是 cte 的一部分,然后我从中删除,因此不可能进行联接。
为什么哦为什么哦为什么
因为缺少索引建议有点糟糕,这就是原因。
唯一最终出现在索引推荐的键中的列是 where 子句中的列。查询中使用的可以从索引排序中受益的其他列被归入包含列。
更糟糕的是,关键列的顺序完全得不到爱或喜爱。当然,它们被分组为等式 (
=
,IS NULL
) 和不等式 (>
,>=
,<
,<=
,<>
,IS NOT NULL
) 谓词,但每组中的列顺序基于表定义中的序号位置。将建议与查询计划中最慢的部分进行比较时,缺少索引可能几乎没有任何好处,并且可能会显示在数据库中无需维护其他对象而很快完成的查询中。
当 SQL Server 竭尽全力为您创建索引时,丢失的索引请求也会丢失。
对于您的问题,更多一点是,索引键列不考虑不可SARGable where 子句表达式,因为不存在寻找这些列中的值的策略,即使它们位于索引的键中。
以下是一些查询示例:
缺少的索引请求有所不同,因为在第二个查询中,谓词 on
LastEditDate
包装在ISNULL
函数中:换句话说,缺失的索引请求有点像一个孩子说他们饿了然后要糖果。
Job_Id
被包含并索引,因为它用于 JOIN 条件。Run_date, Run_time
被包含是因为它们用在最内层子查询条件的表达式中,并且Run_date
也包含在输出列表中,因此不需要排序。它们的值应该从索引中提取,并且服务器在执行此查询时不需要访问表体。我不知道为什么
Instance_id
包含在内 - 它没有在查询中使用。也许相同的索引还改进了使用此列的另一个查询?附言。您使用
TOP 10000
但没有 ORDER BY 子句...您真的需要查询所选择的所有行中的任何 10k 行吗?您不直接使用这些列,它们在表达式中使用。这可以防止任何索引查找,只能进行索引扫描。因此它们可能会被索引或包含在内。但是,当它们被索引时,由于额外的排序步骤,服务器必须在更改数据时执行额外的和不必要的工作。
附言。反转您的条件 - 立即使用列,而从 GETDATE() 获得的引用值将被提取并使用相应的表达式进行格式化。这将改善您的查询。也许这将使这些列的索引得以使用。从另一边,您将获得一个组合 AND 和 OR 的条件,因此索引查找可能会出现问题。你会在实践中进行测试。
聚苯硫醚。将时间戳的日期和时间部分存储在不同的列中并不是一个好的做法。将值存储为一个 DATETIME 并在查询或生成的列中单独计算组件可能更有用。