繁体   English   中英

如何在Sql Server中构造group by的索引

[英]how to structure an index for group by in Sql Server

以下简单查询需要很长时间(几分钟)才能执行。

我有一个索引:

create index IX on [fctWMAUA] (SourceSystemKey, AsAtDateKey)
SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
FROM [fctWMAUA] (NOLOCK) AS [t0]
WHERE SourceSystemKey in (1,2,3,4,5,6,7,8,9)
GROUP BY [t0].[SourceSystemKey]

统计数据如下:

  • 逻辑读取1827978
  • 物理读取1113
  • 预读1806459

采用完全相同的查询并重新格式化如下给我这些统计信息:

  • 逻辑读数36
  • 物理读数0
  • 预读0

执行需要31毫秒。

SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
 FROM [fctWMAUA] (NOLOCK) AS [t0]
 WHERE SourceSystemKey = 1
 GROUP BY [t0].[SourceSystemKey]
UNION
 SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
 FROM [fctWMAUA] (NOLOCK) AS [t0]
 WHERE SourceSystemKey = 2
 GROUP BY [t0].[SourceSystemKey]
UNION
 SELECT MAX([t0].[AsAtDateKey]) AS [Date], [t0].[SourceSystemKey] AS [SourceSystem]
 FROM [fctWMAUA] (NOLOCK) AS [t0]
 WHERE SourceSystemKey = 3
 GROUP BY [t0].[SourceSystemKey]
/* AND SO ON TO 9 */

如何快速制作完成该组的索引?

我发现最好的解决方案如下。 它模仿查询的联合版本,并且运行得非常快。

40个逻辑读取,执行时间为3ms。

SELECT [t3].[value]
FROM [dimSourceSystem] AS [t0]
OUTER APPLY (
    SELECT MAX([t2].[value]) AS [value]
    FROM (
        SELECT [t1].[AsAtDateKey] AS [value], [t1].[SourceSystemKey]
        FROM [fctWMAUA] AS [t1]
        ) AS [t2]
    WHERE [t2].[SourceSystemKey] = ([t0].[SourceSystemKey])
    ) AS [t3]

如果不查看执行计划很难说,但是您可能想尝试以下方法:

SELECT * FROM
(
    SELECT MAX(t0.AsAtDateKey) AS [Date], t0.SourceSystemKey AS SourceSystem
    FROM fctWMAUA (NOLOCK) AS t0
    GROUP BY t0.SourceSystemKey
)
WHERE SourceSystem in (1,2,3,4,5,6,7,8,9)

在没有查看执行计划的情况下很难分辨,但我认为发生的事情是SQL服务器不够聪明,无法意识到指定的WHERE子句是过滤掉组,并且对每个组包含的记录没有任何影响。组。 一旦SQL服务器意识到这一点就可以免费使用一些更智能的索引查找来计算出最大值(这是第二个查询中发生的事情)

只是一个理论,但它可能值得一试。

尝试告诉SQL Server使用索引:

...
FROM [fctWMAUA] (NOLOCK, INDEX(IX)) AS [t0]
...

确保表的统计信息是最新的:

UPDATE STATISTICS [fctWMAUA]

要获得更好的答案,请打开两个查询的showplan:

SET SHOWPLAN_TEXT ON

并将结果添加到您的问题中。

您也可以在没有GROUP BY的情况下编写查询。 例如,您可以使用独有的LEFT JOIN,不包括具有较旧日期的行:

select cur.SourceSystemKey, cur.date
from fctWMAUA cur
left join fctWMAUA next
    on next.SourceSystemKey = next.SourceSystemKey
    and next.date > cur.date
where next.SourceSystemKey is null
and cur.SourceSystemKey in (1,2,3,4,5,6,7,8,9)

这可能会非常快,但我认为它不会击败UNION。

使用HAVING而不是WHERE,以便在发生分组后进行过滤:

SELECT MAX(AsAtDateKey) AS [Date], SourceSystemKey AS SourceSystem
FROM fctWMAUA (NOLOCK)
GROUP BY SourceSystemKey
HAVING SourceSystemKey in (1,2,3,4,5,6,7,8,9)

我也不特别关心IN子句,特别是当它可以替换为“<10”或“1到9之间”时,它们被排序索引更好地使用。

 WHERE SourceSystemKey = 3
 GROUP BY [t0].[SourceSystemKey]

您不需要按固定字段分组。

我更喜欢第一句话。 可能是我会替换的

 WHERE SourceSystemKey in (1,2,3,4,5,6,7,8,9)

喜欢的东西

 WHERE SourceSystemKey BETWEEN 1 AND 9

要么

 WHERE SourceSystemKey >= 1 AND SourceSystemKey <= 9

如果SourceSystemKey是一个整数。 但我认为这不会引起重大变化。

我将首先测试的是重建统计信息并重建表的所有索引并等待一段时间。 重建不是即时的,它将取决于服务器的繁忙程度,但这句话的结构很好,优化器使用的索引。

问候。

您是否尝试在SourceSystemKey列上创建另一个索引? 在where子句中使用该列时,大量的逻辑读取使我认为它正在进行索引/表扫描。 你可以在这个上运行执行计划,看看是否是这种情况? 执行计划也可能提出索引建议。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM