【问题标题】:What is the most efficient way to group by day for overlapping date ranges (SQL Server 2008)?对重叠日期范围(SQL Server 2008)按天分组的最有效方法是什么?
【发布时间】:2015-04-06 03:51:13
【问题描述】:

考虑如下简化表T1:

CREATE TABLE dbo.T1 (
    id        INTEGER       NOT NULL
    ,measure  NUMERIC(15,2) NOT NULL
    ,begin_dt DATE          NOT NULL
    ,end_dt   DATE          NOT NULL
);

假设约束/业务逻辑确保虽然每个 id 可以有多个记录,但单个 id 没有重叠的日期范围,并且单个 id 没有日期范围间隙。例如,

id   | measure |  begin_dt  |   end_dt
-----------------------------------------
1    |  100.00 | 2012-05-07 | 2012-05-30
1    |  200.00 | 2012-05-31 | 2013-10-11
1    |   50.00 | 2013-10-12 | 2013-10-13
1    |    0.00 | 2013-10-14 | 9999-12-31
2    | 1234.56 | 2002-02-25 | 9999-12-31
3    |    9.87 | 2014-01-31 | 2014-02-15
3    |   50.00 | 2014-02-16 | 2015-01-04
3    |    0.00 | 2015-01-05 | 9999-12-31
...

现在,我的目标是生成一个结果集,其中显示 T1 中每个唯一 begin_dt 的一条记录,以及具有正度量值的 id 计数以及该日期介于begin_dt 和 end_dt。因此,类似于以下内容:

    dt     | count_of_ids | sum_of_measure 
-------------------------------------------
2002-02-25 |      1       |   1234.56 
2012-05-07 |      2       |   1334.56 
2012-05-31 |      2       |   1434.56 
2013-10-12 |      2       |   1284.56 
2013-10-14 |      1       |   1234.56 
2014-01-31 |      2       |   1244.43 
2014-02-16 |      2       |   1284.56
2015-01-05 |      1       |   1234.56
... 

我目前的解决方案基本上如下:

SELECT *
FROM (
    SELECT DISTINCT t1.begin_dt AS dt
    FROM dbo.T1 AS t1
) AS dt_s
CROSS APPLY (
    SELECT COUNT(t1.id)     AS count_of_ids
           ,SUM(t1.measure) AS sum_of_measure
    FROM dbo.T1 AS t1
    WHERE t1.measure > 0
          AND dt_s.dt BETWEEN t1.begin_dt AND t1.end_dt
) AS t1_x
ORDER BY dt_s.dt DESC;

这大约需要 3.5 分钟来执行(在具有约 10MM 记录、约 2,500 个唯一日期以及要处理的更多字段、度量和聚合的实际数据集上)-我希望有一种方法可以得到

我尝试了其他方法(使用 UDF / CTE / 等),但它们似乎都遵循相同的执行计划。我对事物的优化方面还没有太多经验,所以我很想听听其他人的最佳通用方法是什么。提前致谢!

【问题讨论】:

  • 你的桌子上有索引吗?
  • 您是否有一个单个索引,其中包含begin_dtmeasure(降序)并包括id? (那就是covering index。请注意,SQL Server 2005 及更高版本支持included columns 以及复合索引。)
  • 我最初在 begin_dt、end_dt 和包括 id 上有一个非聚集索引。我将表格更改为具有@HABO 建议的规格,新结果大约是 5 分钟的执行时间(在设置统计信息 io 时,有 370,337,292 次逻辑读取与之前的 214,776,120 次逻辑读取)。
  • 如何在以下位置建立索引:begin_dtend_dtmeasure(降序)并包括 id。我没有考虑到between 日期。

标签: sql sql-server sql-server-2008 tsql query-optimization


【解决方案1】:

尝试使用以下代码:

SELECT  t1.begin_dt AS dt,COUNT(t2.id) AS count_of_ids,SUM(t1.measure) AS sum_of_measure
    FROM dbo.T1 AS t1
    JOIN dbo.T1 AS t2 ON t1.begin_dt BETWEEN t2.begin_dt AND t2.end_dt
    GROUP BY t1.begin_dt;

通过使用begin_dt、end_dt 上的索引以及转换字段ID 和度量,绝对可以提高性能。 希望这会有所帮助!

【讨论】:

    猜你喜欢
    • 2021-12-24
    • 2013-12-18
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 2014-01-31
    • 1970-01-01
    • 1970-01-01
    • 2013-01-08
    相关资源
    最近更新 更多