sql-server - 了解索引中的包含

标签 sql-server performance t-sql

我有以下查询:

USE [AxReports]
GO

DECLARE @paramCompany varchar(3)
SET @paramCompany = 'adf'

SELECT  stl.MAINSALESID,
        st.DATAAREAID,
        Sum(sl.SALESQTY) as 'Quantity',
        Sum(sl.SALESQTY * sl.SALESPRICE) as 'SalesValue'
INTO #openrel
FROM
    DynamicsV5Realtime.dbo.SALESTABLE st
INNER JOIN
    DynamicsV5Realtime.dbo.SALESLINE sl
ON
    sl.SALESID = st.SALESID
    and sl.DATAAREAID = st.DATAAREAID
INNER JOIN
    DynamicsV5Realtime.dbo.INVENTTABLE it
ON
    it.ITEMID = sl.ITEMID
    and it.DATAAREAID = sl.DATAAREAID
INNER JOIN
    DynamicsV5Realtime.dbo.SALESTABLELINKS stl
ON
    stl.SUBSALESID = st.SALESID
    and stl.DATAAREAID = st.DATAAREAID
WHERE
    st.DATAAREAID = @paramCompany
    and st.SALESTYPE = 3 -- Release Order
    and st.SALESSTATUS = 1
    and sl.SALESSTATUS <> 4
    and it.ITEMGROUPID <> 'G0022A'
GROUP BY
    stl.MAINSALESID,
    st.DATAAREAID

我的执行计划建议索引为:

USE [DynamicsV5Realtime]
GO
CREATE NONCLUSTERED INDEX [<Name of Missing Index, sysname,>]
ON [dbo].[INVENTTABLE] ([DATAAREAID],[ITEMGROUPID])
INCLUDE ([ITEMID])
GO

但是,我已经在该表上有一个索引,该索引与计划正在使用的索引类似,但对其执行表扫描。当前索引如下:

CREATE NONCLUSTERED INDEX [I_ITEMGROUPIDX] ON [dbo].[INVENTTABLE]
(
[ITEMID] ASC,
[DATAAREAID] ASC
)
INCLUDE (   [ITEMGROUPID]) 
GO

我的理解是,只有当您不担心它们在叶级别排序时,才应该将它们作为包含的列(我认为这是正确的?)。

在这种情况下,WHERE 子句有它。ITEMGROUPID <> 'G0022A' 因此将其作为关键列是有意义的,因为按顺序查找该列会更快,(我再次认为我说得对?)

但是对于联接呢,为什么建议将 ITEMID 列作为包含,而不是 DATAAREAID 列?在这种情况下,ITEMID 和 DATAAREAID 构成了 PK,因此这是否与不需要对两列进行排序有关,并且可能使用现有索引,但将 ITEMGROUPID 作为关键列是添加新索引的更好解决方案? (我想这是我可以测试的东西)

谢谢

最佳答案

让我们首先相对孤立地考虑这个表;也就是说,我们只会关注查询中直接提到的那些部分。

执行查询需要执行以下操作:

  1. 查找 INVENTTABLEITEMGROUPID 列等于“G0022A”的所有行。
  2. 查找这些行中 DATAAREAIDITEMID 列的值,以便在 SALESLINE 中查找必要的行。

执行第一个部分的最佳索引是在 ITEMGROUPID 上具有键但没有其他列的索引。这样的键(我们现在将忽略包含的列)将启用表扫描以查找相关行且仅查找相关行。

如果没有这样的索引,但有一个将 ITEMGROUPID 作为其列之一的索引,则可以在表扫描中使用该索引,尽管效率不那么高。

现在,当我们考虑第二部分时,我们真正关心从行中获取的唯一值是 DATAAREAIDITEMID

如果包含这些字段,则可以在索引扫描中使用它们。

如果它们实际上是键的一部分,或者其中之一是并且另一个包含在内,则该索引也可以用于此类索引扫描。

所以。此时,仅考虑我们表示此时会考虑的那些方面并忽略其他考虑因素(索引大小、插入成本等),那么以下任何索引都将在这里有用:

CREATE NONCLUSTERED INDEX [someIndexName]
ON [dbo].[INVENTTABLE] ([ITEMGROUPID],[DATAAREAID],[ITEMID])

CREATE NONCLUSTERED INDEX [someIndexName]
ON [dbo].[INVENTTABLE] ([ITEMGROUPID])
INCLUDE ([DATAAREAID],[ITEMID])

CREATE NONCLUSTERED INDEX [someIndexName]
ON [dbo].[INVENTTABLE] ([ITEMGROUPID])
INCLUDE ([ITEMID],[DATAAREAID])

CREATE NONCLUSTERED INDEX [someIndexName]
ON [dbo].[INVENTTABLE] ([DATAAREAID],[ITEMGROUPID])
INCLUDE ([ITEMID])

CREATE NONCLUSTERED INDEX [someIndexName]
ON [dbo].[INVENTTABLE] ([ITEMID],[ITEMGROUPID])
INCLUDE ([DATAAREAID])

CREATE NONCLUSTERED INDEX [someIndexName]
ON [dbo].[INVENTTABLE] ([ITEMGROUPID],[DATAAREAID])
INCLUDE ([ITEMID])

CREATE NONCLUSTERED INDEX [someIndexName]
ON [dbo].[INVENTTABLE] ([ITEMGROUPID],[ITEMID])
INCLUDE ([DATAAREAID])

这些索引中的每一个都包含 ITEMGROUPID 作为键的全部或部分,并且 ITEMIDDATAAREAID 作为键的任一部分,或作为包含的列。

请注意,您拥有的索引与此相反;它具有理想情况下作为包含列的键的列,以及作为键的一部分的其他列。它总比没有好,查询规划器可以重新调整事物以利用它,但这并不是我们确定想要的理想键。

现在,让我们从整体上考虑查询。

  1. 请注意,我们将根据 DATAAREAID 列搜索 SALESTABLE
  2. 请注意,SALESLINE 在其自己的 DATAAREAID 列上连接到该列。
  3. 请注意,INVENTTABLE 会根据其自己的 DATAAREAID 列依次连接到 SALESLINE 上的该列。

由此我们可以推断,我们在逻辑上只需要来自 INVENTTABLE 的那些在 DATAAREAID 列中具有值 @paramCompany 的记录。

规划者做出了这样的推论。

因此,从整体上考虑查询,我们可以将上面的两个操作更改为:

  1. 查找 INVENTTABLEITEMGROUPID 列等于“G0022A”且 DATAAREAID 等于 @paramCompany 的所有行
  2. 查找这些行中的 DATAAREAID(已在步骤 1 中获取)和 ITEMID 列的值。

因此,理想的索引是:

CREATE NONCLUSTERED INDEX [someName]
ON [dbo].[INVENTTABLE] ([ITEMGROUPID],[DATAAREAID])
INCLUDE ([ITEMID])
GO

或者

CREATE NONCLUSTERED INDEX [someName]
ON [dbo].[INVENTTABLE] ([DATAAREAID],[ITEMGROUPID])
INCLUDE ([ITEMID])
GO

(或者在 key 中包含所有三个 key 的 key ,但如果您实际上不需要它,还有其他原因不使用大 key )。

第二个确实是我们建议你做的。

关于sql-server - 了解索引中的包含,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21333014/

相关文章:

sql-server - Node-odbc 无法连接到 mssql

javascript - jquery.inArray() 与 Object.hasOwnProperty() 之间的性能差异?

sql - 如何删除主表中与临时表中的 ID 匹配的行?

sql-server - 抛出错误的 T-SQL 函数

sql-server - T-SQL NOT IN 有效,但 NOT EXISTS 无效

c# - 以编程方式创建 SQL 数据库

python - 编写以下内容的最快方法

Java switch 足够智能来重新排列吗?

t-sql - SQL Server 查询以获取连续发生的开始和结束

mysql - 这是一个 t sql 存储过程吗