【问题标题】:Does SQL Server jump leaves when using a composite clustered index?使用复合聚集索引时 SQL Server 会跳转吗?
【发布时间】:2010-12-09 14:37:42
【问题描述】:

考虑以下复合聚集索引:

CREATE UNIQUE CLUSTERED INDEX ix_mytable ON mytable(a, b)

显然,b 上的单独索引将使搜索 b 的特定值更快。

但是,如果 b 上的单独索引 被使用,在我看来,复合索引仍然可以用于查找具有特定值的元组 b 不是表扫描,而是通过遍历 a 的离散值树并在本地搜索 b,跳转到下一个值一个

这就是 SQL Server 的工作方式吗? (例如,如果 MSSQL 对具有多列的索引使用单个哈希值,则不会。)

确实如此,并且由于其他原因已经需要复合索引,并且 a 的离散值的数量足够少,性能/空间的权衡可能会偏离拥有 a b 的单独索引。

(上面的 UNIQUE 和 CLUSTERED 约束对于这个例子来说并不是真正需要的,但它们代表了对 b 的最快检索,它不涉及 b的单独索引>--前者为a的每个循环提供了一个快捷方式,后者在查找中删除了一个间接度)。

【问题讨论】:

    标签: sql-server tsql indexing clustered-index


    【解决方案1】:
    USE AdventureWorks2008R2;
    -- Source: http://msftdbprodsamples.codeplex.com/releases/view/59211
    GO
    
    SET NOCOUNT ON;
    GO
    
    CREATE NONCLUSTERED INDEX IX_SalesOrderHeader_OrderDate_#_ShipDate_SubTotal
    ON [Sales].[SalesOrderHeader] ([OrderDate])
    INCLUDE (ShipDate,SubTotal)
    -- WITH(DROP_EXISTING=ON);
    GO
    
    -- Test 1
    SET STATISTICS IO ON;
    SELECT  COUNT(*)
    FROM    Sales.SalesOrderHeader h -- Index Seek on IX_SalesOrderHeader_OrderDate_#_ShipDate_SubTotal
    WHERE   h.OrderDate BETWEEN '2008-07-01T00:00:00.000' AND '2008-07-15T23:59:59.997';
    SET STATISTICS IO OFF;
    GO
    -- End of Test 1
    -- Results:
    -- Table 'SalesOrderHeader'. Scan count 1, logical reads 5, physical reads 0
    
    DROP INDEX IX_SalesOrderHeader_OrderDate_#_ShipDate_SubTotal
    ON [Sales].[SalesOrderHeader]
    GO
    CREATE NONCLUSTERED INDEX [IX_SalesOrderHeader_ShipMethodID_OrderDate_#_ShipDate_SubTotal] 
    ON Sales.SalesOrderHeader 
    (
        ShipMethodID ASC,
        OrderDate ASC
    )
    INCLUDE (ShipDate,SubTotal);
    GO
    
    -- Test 2
    SET STATISTICS IO ON;
    SELECT  COUNT(*)
    FROM    Sales.SalesOrderHeader h -- Index Scan on IX_SalesOrderHeader_ShipMethodID_OrderDate_#_ShipDate_SubTotal
    WHERE   h.OrderDate BETWEEN '2008-07-01T00:00:00.000' AND '2008-07-15T23:59:59.997';
    SET STATISTICS IO OFF;
    GO
    -- End of Test 2
    -- Results:
    -- Table 'SalesOrderHeader'. Scan count 1, logical reads 150, physical reads 0
    
    -- Test 3
    SET STATISTICS IO ON;
    SELECT  COUNT(*)
    FROM    Purchasing.ShipMethod sm
    INNER JOIN Sales.SalesOrderHeader h ON h.ShipMethodID=sm.ShipMethodID -- FK elimination + Index Scan on IX_SalesOrderHeader_ShipMethodID_OrderDate_#_ShipDate_SubTotal
    WHERE   h.OrderDate BETWEEN '2008-07-01T00:00:00.000' AND '2008-07-15T23:59:59.997';
    SET STATISTICS IO OFF;
    GO
    -- End of Test 3
    -- Results:
    -- Table 'SalesOrderHeader'. Scan count 1, logical reads 150, physical reads 0
    
    -- Test 4
    SET STATISTICS IO ON;
    SELECT  MIN(sm.ShipMethodID) AS DummnyCol, -- To prevent FK elimination 
            COUNT(*)
    FROM    Purchasing.ShipMethod sm
    INNER JOIN Sales.SalesOrderHeader h ON h.ShipMethodID=sm.ShipMethodID -- Index Seek on IX_SalesOrderHeader_ShipMethodID_OrderDate_#_ShipDate_SubTotal
    WHERE   h.OrderDate BETWEEN '2008-07-01T00:00:00.000' AND '2008-07-15T23:59:59.997';
    SET STATISTICS IO OFF;
    GO
    -- End of Test 4
    -- Results:
    -- Table 'SalesOrderHeader'. Scan count 5, logical reads 13, physical reads 0
    -- Table 'ShipMethod'. Scan count 1, logical reads 2, physical reads 0
    
    DROP INDEX [IX_SalesOrderHeader_ShipMethodID_OrderDate_#_ShipDate_SubTotal] 
    ON Sales.SalesOrderHeader;
    GO
    SET NOCOUNT OFF;
    GO
    

    【讨论】:

      【解决方案2】:

      不,没有跳过“a”簇。只有指定了最左边的列才能使用索引,否则需要使用全扫描。

      Oracle 有所谓的'Index Skip Scan' 运算符。

      【讨论】:

      • “索引跳过扫描”正是我设想的那种方法。有点失望的是 MSSQL 没有做到这一点,因为它往往是一个比 Oracle 更适合 OOTB 的 RDBMS。
      • 这里是对应的 Microsoft Connect 项:connect.microsoft.com/SQLServer/feedback/details/695044/… 请为它投票。
      • @usr - 只需阅读您的 Connect Item 并点击您的链接返回 Stack Overflow。可以使用递归 CTE 来做一个穷人的版本。 Example syntax here
      • @MartinSmith:谢谢,我已经偶然发现了这一点。由于 CTR,此解决方案不适用于 OR-Mappers。我试图说服微软添加这个功能,因为如果他们这样做会很方便。神奇的是,如果开发人员不做任何事情,某些查询会运行得更快。
      • @usr - 递归 CTE 方法(重复索引搜索)比真正的跳过扫描效率低得多,并且可能需要每组许多行才能使其使用值得考虑。
      猜你喜欢
      • 2011-03-13
      • 1970-01-01
      • 2013-08-20
      • 1970-01-01
      • 2014-01-26
      • 2014-04-27
      • 2022-01-09
      • 1970-01-01
      • 2015-05-30
      相关资源
      最近更新 更多