10

我在 SQL Server 2016 Enterprise Edition 上的所有列上有一个 40m 记录非内存优化表的非聚集列存储索引。

强制使用列存储索引的查询将执行得更快,但优化器继续选择使用聚集索引和其他非聚集索引。我有很多可用的 RAM,并且正在对维度模型使用适当的查询。

为什么优化器不会选择 columnstoreindex?我怎样才能鼓励它的使用(不使用提示)?

这是一个不使用列存储的示例查询:

SELECT
  COUNT(*),
  SUM(TradeTurnover),
  SUM(TradeVolume)
FROM DWH.FactEquityTrade e
--with (INDEX(FactEquityTradeNonClusteredColumnStoreIndex))
JOIN DWH.DimDate d
  ON e.TradeDateId = d.DateId
 JOIN DWH.DimInstrument i
  ON i.instrumentid = e.instrumentid
WHERE d.DateId >= 20160201
AND i.instrumentid = 2

没有提示需要 7 秒,有提示则需要几分之一秒。没有提示的查询计划在这里。带有提示的查询计划在这里

列存储索引的创建语句是:

CREATE NONCLUSTERED COLUMNSTORE INDEX [FactEquityTradeNonClusteredColumnStoreIndex] ON [DWH].[FactEquityTrade]
(
    [EquityTradeID],
    [InstrumentID],
    [TradingSysTransNo],
    [TradeDateID],
    [TradeTimeID],
    [TradeTimestamp],
    [UTCTradeTimeStamp],
    [PublishDateID],
    [PublishTimeID],
    [PublishedDateTime],
    [UTCPublishedDateTime],
    [DelayedTradeYN],
    [EquityTradeJunkID],
    [BrokerID],
    [TraderID],
    [CurrencyID],
    [TradePrice],
    [BidPrice],
    [OfferPrice],
    [TradeVolume],
    [TradeTurnover],
    [TradeModificationTypeID],
    [InColumnStore],
    [TradeFileID],
    [BatchID],
    [CancelBatchID]
)
WHERE ([InColumnStore]=(1))
WITH (DROP_EXISTING = OFF, COMPRESSION_DELAY = 0) ON [PRIMARY]
GO

更新。计划使用 Count(EquityTradeID) 而不是 Count(*) 并包含提示

4

3 回答 3

5

您要求 SQL Server 选择一个复杂的查询计划而不是一个简单的查询计划。请注意,使用提示时,SQL Server 必须将列存储索引与行存储非聚集索引 ( IX_FactEquiteTradeInColumnStore) 连接起来。仅使用行存储索引时,它可以进行查找(我假设TradeDateId是该索引的前导列)。它仍然需要进行密钥查找,但它更简单。

我可以在没有提示的情况下看到两个选项来获得这种行为:

首先,InColumnStore从列存储索引定义中移除并覆盖整个表。这就是您对列存储的要求 - 涵盖所有内容。

如果这不可能,您可以使用 aUNION ALL显式拆分数据:

WITH workaround
     AS (
         SELECT TradeDateId
              , instrumentid
              , TradeTurnover
              , TradeVolume
         FROM DWH.FactEquityTrade
         WHERE InColumnStore = 1
         UNION ALL
         SELECT TradeDateId
              , instrumentid
              , TradeTurnover
              , TradeVolume
         FROM DWH.FactEquityTrade
         WHERE InColumnStore = 0 -- Assuming this is a non-nullable BIT
        )
     SELECT COUNT(*)
          , SUM(TradeTurnover)
          , SUM(TradeVolume)
     FROM workaround e
          JOIN DWH.DimDate d
            ON e.TradeDateId = d.DateId
          JOIN DWH.DimInstrument i
            ON i.instrumentid = e.instrumentid
     WHERE d.DateId >= 20160201
           AND i.instrumentid = 2;
于 2017-05-16T16:55:45.950 回答
3

您的索引是一个过滤索引(它有一个WHERE谓词)。

只有当查询WHERE匹配索引的WHERE. 这适用于经典索引,很可能适用于列存储索引。当优化器不使用过滤索引时,可能会有其他限制。

因此,要么添加WHERE ([InColumnStore]=(1))到您的查询中,要么将其从索引定义中删除。

您在评论中说:“InColumnStore 过滤器是为了在加载数据时提高效率。到目前为止,对于所有测试,过滤器覆盖了所有行的 100%”。这里的“所有行”是指“整个表的所有行”还是只是“结果集的所有行”?无论如何,优化器很可能不知道这一点(即使它可以从统计中得出),这意味着使用此类索引的计划必须明确地进行额外的检查/查找,优化器认为这太昂贵了。

以下是有关此主题的几篇文章:

为什么没有使用我的过滤索引?通过罗布法利

Paul White过滤索引的优化器限制。

Paul White添加过滤索引的意外副作用。

Aaron Bertrand的过滤索引如何成为更强大的功能,请参阅优化器限制部分。

于 2017-05-17T07:21:59.247 回答
-1

试试这个:桥接您的查询

Select * 
Into #DimDate
From DWH.DimDate
WHERE DateId >= 20160201

Select  COUNT(1), SUM(TradeTurnover), SUM(TradeVolume)
From DWH.FactEquityTrade e
Inner Join DWH.DimInstrument i ON i.instrumentid = e.instrumentid 
     And i.instrumentid = 2
Left Join #DimDate d ON e.TradeDateId = d.DateId

这个查询的运行速度有多快?

于 2017-05-19T02:32:57.480 回答