在Postgres数据库中,我有与id相关联的"事件"条目,以及它们发生的时间.我需要用一条特殊的规则来数一数.

当事件发生时,计数器递增,并且在接下来的14天内,不会对此类型的所有事件进行计数.

示例:

event created_at blockdate action
16 2021-11-11 11:15 25.11.21 count
16 2021-11-11 11:15 25.11.21 block
16 2021-11-13 10:45 25.11.21 block
16 2021-11-16 10:40 25.11.21 block
16 2021-11-23 11:15 25.11.21 block
16 2021-11-23 11:15 25.11.21 block
16 2021-12-10 13:00 24.12.21 count
16 2021-12-15 13:25 24.12.21 block
16 2021-12-15 13:25 24.12.21 block
16 2021-12-15 13:25 24.12.21 block
16 2021-12-20 13:15 24.12.21 block
16 2021-12-23 13:15 24.12.21 block
16 2021-12-31 13:25 14.01.22 count
16 2022-02-05 15:00 19.02.22 count
16 2022-02-05 15:00 19.02.22 block
16 2022-02-13 17:15 19.02.22 block
16 2022-02-21 10:09 07.03.22 count
43 2021-11-26 11:00 10.12.21 count
43 2022-01-01 15:00 15.01.22 count
43 2022-04-13 10:07 27.04.22 count
43 2022-04-13 10:09 27.04.22 block
43 2022-04-13 10:09 27.04.22 block
43 2022-04-13 10:09 27.04.22 block
43 2022-04-13 10:10 27.04.22 block
43 2022-04-13 10:10 27.04.22 block
43 2022-04-13 10:47 27.04.22 block
43 2022-05-11 20:25 25.05.22 count
75 2021-10-21 12:50 04.11.21 count
75 2021-11-02 12:50 04.11.21 block
75 2021-11-18 11:15 02.12.21 count
75 2021-11-18 12:55 02.12.21 block
75 2021-11-18 16:35 02.12.21 block
75 2021-11-24 11:00 02.12.21 block
75 2021-12-01 11:00 02.12.21 block
75 2021-12-14 13:25 28.12.21 count
75 2021-12-15 13:35 28.12.21 block
75 2021-12-26 13:25 28.12.21 block
75 2022-01-31 15:00 14.02.22 count
75 2022-02-02 15:30 14.02.22 block
75 2022-02-03 15:00 14.02.22 block
75 2022-02-17 15:00 03.03.22 count
75 2022-02-17 15:00 03.03.22 block
75 2022-02-18 15:00 03.03.22 block
75 2022-02-23 15:00 03.03.22 block
75 2022-02-25 15:00 03.03.22 block
75 2022-03-04 10:46 18.03.22 count
75 2022-03-08 21:05 18.03.22 block

在Excel中,我只需添加两列.在一篇专栏文章中,我延续了一个"封杀日期",这个日期指的是必须封杀事件的日期.在另一栏中,我将ID与先前的ID和先前的"阻止日期"进行了比较.

当ID不同或区块日期早于当前日期时,我必须计算.当我必须计算时,我将行的阻止日期设置为当前日期+14天,否则我将延续上一个阻止日期.

我现在试着在波斯格雷斯解决这个问题...

  • 窗口函数
  • 递归CTE
  • 侧向连接

...一切似乎都有些希望,但最终我未能实现这一棘手的计算.

例如,我的递归CTE失败,错误为:

在WHERE中不允许使用聚合函数

with recursive event_count AS (
        select event
             , min(created_at) as created
        from test
        group by event
        
        union all
        
        ( select event
               , created_at as created
          from test
          join event_count
          using(event)
          where created_at >= max(created) + INTERVAL '14 days'
          order by created_at
          limit 1
        )
)
select * from event_count

使用lag()访问前一行的窗口函数似乎不起作用,因为它们不能访问使用窗口函数创建的前一行中的列.

在输入新的事件条目时,通过简单地与上一个条目进行比较来添加"块或计数"信息并不能解决这个问题,因为事件条目大约半年后就会"消失".因此,当第一个条目消失时,下一个条目就成为第一个条目,而逻辑必须应用于新的情况.

上述测试数据可通过以下方式创建:

CREATE TABLE test ( 
  event      INTEGER, 
  created_at TIMESTAMP
);

INSERT INTO test (event, created_at) VALUES
(16, '2021-11-11 11:15'),(16, '2021-11-11 11:15'),(16, '2021-11-13 10:45'),(16, '2021-11-16 10:40'),
(16, '2021-11-23 11:15'),(16, '2021-11-23 11:15'),(16, '2021-12-10 13:00'),(16, '2021-12-15 13:25'),
(16, '2021-12-15 13:25'),(16, '2021-12-15 13:25'),(16, '2021-12-20 13:15'),(16, '2021-12-23 13:15'),
(16, '2021-12-31 13:25'),(16, '2022-02-05 15:00'),(16, '2022-02-05 15:00'),(16, '2022-02-13 17:15'),
(16, '2022-02-21 10:09'),
(43, '2021-11-26 11:00'),(43, '2022-01-01 15:00'),(43, '2022-04-13 10:07'),(43, '2022-04-13 10:09'),
(43, '2022-04-13 10:09'),(43, '2022-04-13 10:09'),(43, '2022-04-13 10:10'),(43, '2022-04-13 10:10'),
(43, '2022-04-13 10:47'),(43, '2022-05-11 20:25'),
(75, '2021-10-21 12:50'),(75, '2021-11-02 12:50'),(75, '2021-11-18 11:15'),(75, '2021-11-18 12:55'),
(75, '2021-11-18 16:35'),(75, '2021-11-24 11:00'),(75, '2021-12-01 11:00'),(75, '2021-12-14 13:25'),
(75, '2021-12-15 13:35'),(75, '2021-12-26 13:25'),(75, '2022-01-31 15:00'),(75, '2022-02-02 15:30'),
(75, '2022-02-03 15:00'),(75, '2022-02-17 15:00'),(75, '2022-02-17 15:00'),(75, '2022-02-18 15:00'),
(75, '2022-02-23 15:00'),(75, '2022-02-25 15:00'),(75, '2022-03-04 10:46'),(75, '2022-03-08 21:05');

推荐答案

这适合于过程性解决方案,因为它必须遍历每个事件现有行的整个历史.但SQL也可以做到这一点.

The best solution heavily depends on cardinalities, data distribution, and other circumstances.
Assuming unfavorable conditions:

  • 大桌子.
  • 相关事件(事件ID)的数量和身份未知.
  • 每个事件有很多行.
  • 有些与14天的时间框架重叠,有些不重叠.
  • 可能有任意数量的副本.

你需要像这样的index分:

CREATE INDEX test_event_created_at_idx ON test (event, created_at);

则下面的查询emulates an index-skip scan.如果桌子足够吸尘,它一次就能以index-only scans exclusively的速度运行:

WITH RECURSIVE hit AS (
   (
   SELECT event, created_at
   FROM   test
   ORDER  BY event, created_at
   LIMIT  1
   )
   
   UNION ALL
   SELECT t.*
   FROM   hit h
   CROSS  JOIN LATERAL (
      SELECT t.event, t.created_at
      FROM   test t
      WHERE  (t.event, t.created_at)
           > (h.event, h.created_at + interval '14 days')
      ORDER  BY t.event, t.created_at
      LIMIT  1
      ) t
   )
SELECT count(*) AS hits FROM hit;

fiddle

我怎么强调都不为过,它将会是100年.:)

它是使用LATERAL子查询的recursive CTE,全部基于ROW value comparison的魔力(并不是所有主要的RDBMS都正确支持ROW value comparison).

实际上,我们让postgres跳过上面的索引一次,并且只接受符合条件的行.

有关详细说明,请参阅:

不同的方法?

就像您提到自己一样,不幸的任务定义迫使您为旧数据更改的事件重新计算所有较新的行.

请考虑改用常量栅格.就像从每年1月1日开始的14天网格.然后,每个事件的状态可以从本地帧中得到.便宜得多,也更可靠.

Sql相关问答推荐

使用Mac日志(log)时间找出SQL中的好小时和坏小时

当一个视图在Postgres中失效时?

每组显示一行(表1中的分组值),表2中的不同列表用逗号分隔

SQL:计算与另一列中给定值互斥的匹配项

收到%1、%2或%2邮箱的唯一客户

返回UPSERT中的旧行值

在SQL中为两个日期之间的每个日期添加行

明细表中没有记录如何更新主表的值为0

按行值出现的顺序对行值进行分组

根据具有特定值的 ID 创建自定义组

从输出中删除 jsonb_build_object

MySQL中的递归查询邻接表深度优先?

验证某个日期前后的连续代码

Set vs let vs 在snowflake中声明变量

As400 (IBM i) SQL 表 QSYS2.SYSTABLES 上的元数据

在 MySql 数据库中的两个日期之间搜索

SQL 中的问题与包含最大日期的记录连接

聚合 Athena 中的列

在 UTF 编码字符上应用 SQL LIKE 语句没有给出任何结果

Snowflake SQL group-by 的行为不同,具体取决于列是按位置引用还是按别名引用