我有以下查询:
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 作为关键列是添加新索引的更好解决方案? (我想这是我可以测试的东西)
谢谢
最佳答案
让我们首先相对孤立地考虑这个表;也就是说,我们只会关注查询中直接提到的那些部分。
执行查询需要执行以下操作:
- 查找
INVENTTABLE
中ITEMGROUPID
列等于“G0022A”的所有行。 - 查找这些行中
DATAAREAID
和ITEMID
列的值,以便在SALESLINE
中查找必要的行。
执行第一个部分的最佳索引是在 ITEMGROUPID
上具有键但没有其他列的索引。这样的键(我们现在将忽略包含的列)将启用表扫描以查找相关行且仅查找相关行。
如果没有这样的索引,但有一个将 ITEMGROUPID
作为其列之一的索引,则可以在表扫描中使用该索引,尽管效率不那么高。
现在,当我们考虑第二部分时,我们真正关心从行中获取的唯一值是 DATAAREAID
和 ITEMID
。
如果包含这些字段,则可以在索引扫描中使用它们。
如果它们实际上是键的一部分,或者其中之一是并且另一个包含在内,则该索引也可以用于此类索引扫描。
所以。此时,仅考虑我们表示此时会考虑的那些方面并忽略其他考虑因素(索引大小、插入成本等),那么以下任何索引都将在这里有用:
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
作为键的全部或部分,并且 ITEMID
和 DATAAREAID
作为键的任一部分,或作为包含的列。
请注意,您拥有的索引与此相反;它具有理想情况下作为包含列的键的列,以及作为键的一部分的其他列。它总比没有好,查询规划器可以重新调整事物以利用它,但这并不是我们确定想要的理想键。
现在,让我们从整体上考虑查询。
- 请注意,我们将根据
DATAAREAID
列搜索SALESTABLE
。 - 请注意,
SALESLINE
在其自己的DATAAREAID
列上连接到该列。 - 请注意,
INVENTTABLE
会根据其自己的DATAAREAID
列依次连接到SALESLINE
上的该列。
由此我们可以推断,我们在逻辑上只需要来自 INVENTTABLE
的那些在 DATAAREAID
列中具有值 @paramCompany
的记录。
规划者做出了这样的推论。
因此,从整体上考虑查询,我们可以将上面的两个操作更改为:
- 查找
INVENTTABLE
中ITEMGROUPID
列等于“G0022A”且DATAAREAID
等于@paramCompany 的所有行
。 - 查找这些行中的
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/