简单的内部联接建议包含索引

时间:2017-08-15 09:43:33

标签: sql sql-server sql-server-2012 query-performance sql-execution-plan

我有这个简单的内连接查询,其执行计划主表有大约34K记录,详细表有大约51K记录。但是这个简单的查询建议添加一个带有include的索引(包含我在select中包含的所有主列)。我没想到这可能是原因和补救措施。

   DECLARE    
       @StartDrInvDate Date ='2017-06-01',
       @EndDrInvDate Date='2017-08-31'

   SELECT
       Mastertbl.DrInvoiceID,
       Mastertbl.DrInvoiceNo,
       Mastertbl.DistributorInvNo,
       PreparedBy,
       detailtbl.BatchNo, detailtbl.Discount,
       detailtbl.TradePrice, detailtbl.IssuedUnits,
       detailtbl.FreeUnits
   FROM 
       scmDrInvoices Mastertbl
   INNER JOIN 
       scmDrInvoiceDetails detailtbl ON Mastertbl.DrInvoiceID = detailtbl.DrInvoiceID
   WHERE
       (Mastertbl.DrInvDate BETWEEN @StartDrInvDate AND @EndDrInvDate)

enter image description here

我真正的好奇心是为什么它建议这个索引 - 我通常不会看到更大表的这种行为

2 个答案:

答案 0 :(得分:1)

对于此查询:

SELECT m.DrInvoiceID, m.DrInvoiceNo, m.DistributorInvNo,
       PreparedBy,
       d.BatchNo, d.Discount, d.TradePrice, d.IssuedUnits, d.FreeUnits
FROM scmDrInvoices m INNER JOIN 
     scmDrInvoiceDetails d
     ON m.DrInvoiceID = d.DrInvoiceID
WHERE m.DrInvDate BETWEEN @StartDrInvDate AND @EndDrInvDate;

我希望基本索引为:scmDrInvoices(DrInvDate, DrInvoiceID)scmDrInvoiceDetails(DrInvoiceID)。此索引将允许查询引擎快速识别与主表中的WHERE匹配的行,然后在scmDrInvoiceDetails中查找相应的值。

其余列可以包含在任一索引中,因此索引将覆盖查询。 "封面"表示所有列都在索引中,因此查询计划不需要引用原始数据页。

上述策略是SQL Server的建议。

答案 1 :(得分:1)

您或许可以看到为什么建议对发票日期编制索引的逻辑;它根据它认为当前的行数对你想要的行数进行了一些计算,并且看起来索引对该列的选择性使其值得索引。如果你想要55,000中的3行,并且你每5分钟想要它,那么索引是有意义的。特别是如果该表的增长率意味着明年它将是550万中的3行。

包含建议可能更天真地建议将足够的附加数据与索引值相关联,以便可以从索引中回答主表所需的整个数据集,而无需访问表 - 索引本质上是指向表中行的指针;当查询引擎使用索引来定位它将需要的所有行时,它仍然需要对表进行打击以实际获取所需的数据。通过在索引中包含数据,您可以删除转到表的必要性,有时它是合理的,但不是其他的(创建许多索引,基本上复制大多数/所有表数据,很少运行查询是浪费磁盘空间)。

同样要考虑的是,现在,在调试工具中运行此查询的频率正在影响SQLServer对查询使用频率的看法。我经常发现我的SQLAzure门户网站提出了索引建议,这要归功于开发人员一遍又一遍地运行查询,调试它,当我实际知道prod时,该查询将每月使用一次,所以我放弃了建立索引的建议包括大多数表格,当直接“只搜索列的索引”会很好,没有必要的

因此,不应盲目注意这些建议,因为SQLServer无法知道您打算在现实应用程序中使用此内容或类似查询。应仔细,周密地完成索引的创建和维护;例如,可能是这个查询要求这个索引,另一个查询会想要一个不同列的索引,但是创建一个在两列上按键(以特定顺序)然后在任何查询搜索中的索引都是有意义的在第二个索引的列上,包括一个命中第一个索引列的谓词,无论查询是否需要它

例如,在您的发票表中,您有一个列,指示是否已付款,以及您应用中的其他位置还有另一个查询,用于计算未付发票的数量。您可以有两个索引 - 一个在发票日期(对于此查询),一个在状态(对于该查询)或一个在两个列(状态,日期),并且在此查询中具有WHERE status = 'unpaid' AND date between...的谓词,即使状态谓词是多余的。为什么它可能是多余的?假设你知道你只会选择上周尚未寄出的发票,所以只能付不起......这就是我所说的“对索引进行深思熟虑” - 你对你的应用了解很多SQLServer永远无法弄清楚..通过在“从上周获取发票”查询中包含冗余状态列(即使状态在逻辑上是冗余的),您允许查询引擎使用首先按状态排序的索引,然后按日期排序。这意味着您只需维护一个索引即可逃脱,并且可以由两个查询使用

索引维护和创建逻辑可以是全职工作..;)