【发布时间】:2019-03-19 00:32:31
【问题描述】:
我需要找到一种有效的方法来创建查询报告聚合的增量,以及值的开始和结束日期。
要求
- 源表包括开始日期、结束日期、类别 ID、子类别 ID 以及子类别是否处于活动状态的指示符。
- 聚合是针对cat_id上的is_active,只要is_active的任何sub_category也为1,函数的结果应该为1。
- 如果聚合函数的结果对于连续的日期范围相同,则应合并日期范围以减少结果集。
- 类别/子类别组合永远不会有重叠的日期,但其他子类别可能会跨越彼此的界限。
我的尝试
我尝试创建一个 CTE,为一个类别生成所有可能的范围,然后重新连接到主查询,以便分解跨越多个范围的子类别。然后我按范围分组并执行了 MAX(is_active)。
虽然这是一个好的开始(此时我需要做的就是将具有相同值的连续范围组合起来),但查询速度非常慢。我对 Postgres 的熟悉程度不如对其他 SQL 风格的熟悉,因此我决定最好花时间与更有经验的人联系并寻求帮助。
源数据
+----+------------+------------+--------+------------+-----------+-----------------------------------------------------+
| id | start_dt | end_dt | cat_id | sub_cat_id | is_active | comment |
+----+------------+------------+--------+------------+-----------+-----------------------------------------------------+
| 1 | 2018-01-01 | 2018-01-31 | 1 | 1001 | 1 | (null) |
| 2 | 2018-02-01 | 2018-02-14 | 1 | 1001 | 0 | (null) |
| 3 | 2018-02-15 | 2018-02-28 | 1 | 1001 | 0 | cat 1 is_active is unchanged despite new record. |
| 4 | 2018-03-01 | 2018-03-30 | 1 | 1001 | 1 | (null) |
| 5 | 2018-01-01 | 2018-01-15 | 2 | 2001 | 1 | (null) |
| 6 | 2018-01-01 | 2018-01-31 | 2 | 2002 | 1 | (null) |
| 7 | 2018-01-15 | 2018-02-10 | 2 | 2001 | 0 | cat 2 should still be active until 2002 is inactive |
| 8 | 2018-02-01 | 2018-02-14 | 2 | 2002 | 0 | cat 2 is inactive |
| 9 | 2018-02-10 | 2018-03-15 | 2 | 2001 | 0 | this record will cause trouble |
| 10 | 2018-02-15 | 2018-03-30 | 2 | 2002 | 1 | cat 2 should be active again |
| 11 | 2018-03-15 | 2018-03-30 | 2 | 2001 | 1 | cat 2 is_active is unchanged despite new record. |
| 12 | 2018-04-01 | 2018-04-30 | 2 | 2001 | 0 | cat 2 ends in a zero |
+----+------------+------------+--------+------------+-----------+-----------------------------------------------------+
预期结果
+------------+------------+--------+-----------+
| start_dt | end_dt | cat_id | is_active |
+------------+------------+--------+-----------+
| 2018-01-01 | 2018-01-31 | 1 | 1 |
| 2018-02-01 | 2018-02-28 | 1 | 0 |
| 2018-03-01 | 2018-03-30 | 1 | 1 |
| 2018-01-01 | 2018-01-31 | 2 | 1 |
| 2018-02-01 | 2018-02-14 | 2 | 0 |
| 2018-02-15 | 2018-03-30 | 2 | 1 |
| 2018-04-01 | 2018-04-30 | 2 | 0 |
+------------+------------+--------+-----------+
这里有一个 select 语句可以帮助您编写自己的测试。
SELECT id,start_dt::date start_date,end_dt::date end_date,cat_id,sub_cat_id,is_active::int is_active,comment
FROM (VALUES
(1, '2018-01-01', '2018-01-31', 1, 1001, '1', null),
(2, '2018-02-01', '2018-02-14', 1, 1001, '0', null),
(3, '2018-02-15', '2018-02-28', 1, 1001, '0', 'cat 1 is_active is unchanged despite new record.'),
(4, '2018-03-01', '2018-03-30', 1, 1001, '1', null),
(5, '2018-01-01', '2018-01-15', 2, 2001, '1', null),
(6, '2018-01-01', '2018-01-31', 2, 2002, '1', null),
(7, '2018-01-15', '2018-02-10', 2, 2001, '0', 'cat 2 should still be active until 2002 is inactive'),
(8, '2018-02-01', '2018-02-14', 2, 2002, '0', 'cat 2 is inactive'),
(9, '2018-02-10', '2018-03-15', 2, 2001, '0', 'cat 2 is_active is unchanged despite new record.'),
(10, '2018-02-15', '2018-03-30', 2, 2002, '1', 'cat 2 should be active agai'),
(11, '2018-03-15', '2018-03-30', 2, 2001, '1', 'cat 2 is_active is unchanged despite new record.'),
(12, '2018-04-01', '2018-04-30', 2, 2001, '0', 'cat 2 ends in 0.')
) src ( "id","start_dt","end_dt","cat_id","sub_cat_id","is_active","comment" )
【问题讨论】:
-
所需的逻辑看起来相当复杂,我并没有试图理解所有内容,但您可能会发现 Itzik Ben-Gan 的以下文章很有帮助:Packing Intervals。它是为 SQL Server 编写的,但 Postgres 具有它使用的所有功能(如窗口函数),因此它也可以在 Postgres 中使用。您还可以添加一些解释来解释为什么您会以这个日期间隔结束:
2018-02-01 | 2018-02-14在cat_id = 2的预期结果中。你是怎么得到这些日期的? -
我试图从现实世界的问题中简化这个问题,因为这太复杂了——也许我把它弄得太抽象了?感谢您的博客文章,我会检查一下。要回答您的第二个问题,2018-02-01 - 2018-02-14 为 0,因为在此期间子目录 2001 和 2002 均为 0。在 2018 年 2 月 15 日,2001 年为 1,因此 cat 2 为 is_active 为 1。
标签: sql postgresql