Does SQL Server jump leaves when using a composite clustered index?
Consider the following composite clustered index:
CREATE UNIQUE CLUSTERED INDEX ix_mytable ON mytable(a, b)
Obviously, a separate index on b will make searching for a particular value of b faster.
However, if a separate index on b is not employed, it seems to me that the composite index can still be used to find tuples with a particular value for b instead of a table scan, by traversing the tree of discrete values of a and do a local search for b, jump to the next value of a, etc.
Is this how SQL Server works? (It would not be, for instance, if MSSQL uses a single hash value for indexes with multiple columns.)
It it is, and the composite index is needed already for other reasons, and t开发者_C百科he number of discrete values of a is small enough, the performance/space trade-off may swing away from having a separate index for b.
(The UNIQUE and CLUSTERED constraints above aren't really required for this example, but they would represent the fastest retrieval of b that did not involve a separate index for b--the former providing a shortcut for each loop of a, the latter removing one degree of indirection in the lookup).
No, there is no jumping over a clusters of 'a'. An index can be used only if the leftmost column is specified, otherwise a full scan needs to be employed.
Oracle has the so called 'Index Skip Scan' operator.
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
精彩评论