使用 COUNT(DISTINCT ...) 为查询索引和管理表

Indexing and managing tables for queries with COUNT(DISTINCT ...)

我有几个大型(~10 亿行,~100GB)存档 tables,其中包含用于分析目的的客户行为日志(还没有仓库,正在进行中)。

每个每天从每日日志中填充一次 table 并包含当年的数据。

许多请求要求查询计算不同时间范围(从每小时到每月、3 个月、上一季度、当年)的不同值的查询,这就是我们将当年全部放在一个地方的原因,但是行数开始变得荒谬。

SELECT 
    CAST(datetime as date), 
    element, 
    COUNT(DISTINCT client_id), 
    COUNT(DISTINCT session_id), 
    COUNT(*)
FROM dbo.pageviews
WHERE DATETIME >= ''
   AND DATETIME < ''
GROUP BY CAST(DATETIME as date), element

其他时候,我们必须提取给定时间段内给定 client_idsession_id 的所有日志。

我们有一些遗留索引(包含多个列的非聚集索引,索引的大小是 table 的数倍)。

在等待仓库的过程中,我想稍微改善一下情况,所以我开始进行一些基本的更改。

问题一:

我在 datetime 列上添加了聚集索引(以帮助查询)并在 servetime 上添加了非聚集索引(以帮助每日插入)。这是正确的还是应该相反?

问题二:

如果我们将 tables 分成,比如说,每月的块,并针对一个 VIEW 执行查询,所有这些都作为 UNION ALLdatetime 作为聚簇索引,会不会如果我们经常在跨越多个 tables?

的时间段内计算 COUNT(distinct X),这会有所帮助

任何其他可以在短期内帮助管理此问题的修复程序?

测试用例: 我在 3 个月的时间内使用各种索引测试了上述查询,得到了以下结果:

编辑:附加信息:

您为 "datetime" 列创建聚集索引是正确的。如果不定期搜索 "servetime" 列,则非聚集索引没有太大帮助。
您在这里需要的是 分区 您的 table 并且可能会显着提高性能。它在逻辑上拆分您的 tables 数据,因此您无需更改任何现有查询,同时受益于拆分数据。
分区是一个复杂的概念。您可以找到有用的信息 here.

您的问题很复杂;它可能太宽泛了。但它有一个简单的解决方案,在 datetime 列上进行分区,因为这是用于查询表的列。我只想指出一些与此相关的高级问题。

但是,更简单的分区列是 servertime——这将允许您只交换分区的进出。但是,这是以增加查询难度为代价的。如果您知道 datetime 总是在一个分区中,例如其值的三天内,您就可以完成这项工作。

一些数据库限制了您可以插入的 "open" 分区的数量。我不认为SQL服务器有这个限制。

但是,您会遇到另一个问题。 结果可能会随着时间的推移而改变。 因此,如果您要计算 2019-10-31 的任何数量。然后,您可以在 2019-11-01 和 2019-11-02 获得不同的值,依此类推,因为数据不断涌入。

如果您将数据用于需要静态的内容(例如财务报告),这可能是个大问题。您可能希望在查询中包含任意限制。像这样:

select *
from . . .
where partition_date = '2019-10-31' and
      abs(datediff(day, servertime, datetime)) < 7;

请注意,我添加了一个伪列 partition_date 只是为了清楚什么用于分区。您可以为此直接使用 datetime

也就是一周内有数据进来。您没有指定 servertime 是否大于 datetime。请注意,即使您认为这不是由于计算机上的时间漂移​​和可能的时区问题,这也是可能的。