了解包含在索引中

时间:2014-01-24 12:41:51

标签: sql-server performance tsql

我有以下查询:

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子句具有it.ITEMGROUPID&lt;&gt; 'G0022A'所以把它作为一个关键专栏是有道理的,因为按顺序寻找那个专栏会更快,(我认为我说的是对的吗?)

然而,为什么连接,为什么建议将ITEMID列作为包括而不是DATAAREAID列?在这种情况下,ITEMID和DATAAREAID组成了PK,因此它不需要对两列进行排序,也许可以使用现有索引,但将ITEMGROUPID作为关键列,是添加新索引的更好解决方案吗? (这就是我可以测试的东西)

由于

2 个答案:

答案 0 :(得分:3)

让我们先考虑相对隔离的这个表;那就是我们只关注那些直接提到的查询部分。

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

  1. 查找INVENTTABLE列中等于'G0022A'的ITEMGROUPID中的所有行。
  2. 查找这些行中DATAAREAIDITEMID列的值,以便在SALESLINE中查找必要的行。
  3. 执行第一部分的最佳索引是ITEMGROUPID但没有其他列的密钥。这样的密钥(我们现在将忽略包含的列)将使表扫描能够找到相关的行和仅那些行。

    如果没有这样的索引但是有一个索引的ITEMGROUPID作为其列之一,那么该索引可以用于表扫描,但效果不是很好。

    现在,当我们考虑第二部分时,我们实际关心的唯一值是DATAAREAIDITEMID

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

    如果它们实际上是密钥的一部分,或者其中一个是,而另一个是,则该索引也可用于此类索引扫描。

    因此。在这一点上,只考虑我们在此时会考虑的那些方面并忽略其他考虑因素(索引大小,插入成本等),那么以下任何索引在这里都很有用: / p>

    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. 请注意,我们会根据SALESTABLE列搜索DATAAREAID
    2. 请注意,SALESLINE已在其自己的DATAAREAID列上加入该列。
    3. 请注意INVENTTABLE依据自己的SALESLINE列加入DATAAREAID上的该列。
    4. 由此我们可以推断,我们逻辑上只希望来自INVENTTABLE的{​​{1}}列中包含值为@paramCompany的记录。

      计划者做了这个演绎。

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

      1. 查找DATAAREAID列中INVENTTABLE列等于'G0022A'且ITEMGROUPID等于DATAAREAID的所有行。
      2. 查找这些行中@paramCompany(已在步骤1中获得)和DATAAREAID列的值。
      3. 因此理想的指数是:

        ITEMID

        OR

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

        (或者包含密钥中所有三个密钥的密钥,但如果您实际上不需要密钥,则还有其他原因没有大密钥。)

        第二个确实是你的建议。

答案 1 :(得分:0)

对Google来说这应该很容易,但我想说基本上只有索引中的连接中使用的列并包含返回列,这样就不需要对实际表进行查找(包括在索引中)。 我会说建议可能或多或少可靠,可能是因为统计数据不好或其他什么,不要盲目依赖它们。此外,我认为当运算符为'&lt;&gt;'时,不能使用索引。