PostgreSQL - 检测系列中的模式

时间:2014-10-28 14:25:41

标签: sql postgresql

考虑下表:

id | date       | status
1  | 2014-01-10 | 1
1  | 2014-02-10 | 1
1  | 2014-03-10 | 1
1  | 2014-04-10 | 1
1  | 2014-05-10 | 0
1  | 2014-06-10 | 0
------------------------
2  | 2014-01-10 | 1
2  | 2014-02-10 | 1
2  | 2014-03-10 | 0
2  | 2014-04-10 | 1
2  | 2014-05-10 | 0
2  | 2014-06-10 | 0
------------------------
3  | 2014-01-10 | 1
3  | 2014-02-10 | 0
3  | 2014-03-10 | 0
3  | 2014-04-10 | 1
3  | 2014-05-10 | 0
3  | 2014-06-10 | 0
------------------------
4  | 2014-01-10 | 0
4  | 2014-02-10 | 1
4  | 2014-03-10 | 1
4  | 2014-04-10 | 1
4  | 2014-05-10 | 0
4  | 2014-06-10 | 0
------------------------
5  | 2014-01-10 | 0
5  | 2014-02-10 | 1
5  | 2014-03-10 | 0
5  | 2014-04-10 | 1
5  | 2014-05-10 | 0
5  | 2014-06-10 | 0
------------------------

Id字段是用户ID,日期字段是某个检查点到期时的状态,状态指示检查点是否由其用户完成。

我在尝试检测跳过某些检查点的用户时遇到了很大的麻烦,比如有ids 2,3,4和5的用户。实际上我需要一个查询,列出中间缺少检查点的ID系列的开头,只返回ID。

我已经努力找到一种方法,只是查询,但我无法创建一个。我知道我可以编写一些脚本,但我正在处理的项目要求我只使用SQL。

任何人对如何实现这一点有任何想法?

编辑:根据mods的建议,这里有更多细节和一些我尝试失败的事情:

我最成功的尝试是计算使用此查询为每个ID注册了多少个状态:

SELECT
    id,
    SUM(CASE WHEN status = 1 THEN 1 ELSE 0 END) AS check,
    SUM(CASE WHEN status = 0 THEN 1 ELSE 0 END) AS non_check
FROM
    example_table
GROUP BY
    id
ORDER BY
    id

获得以下结果:

id | check | non_check
1  | 4     | 2
2  | 3     | 3
3  | 2     | 4
4  | 3     | 3
5  | 2     | 4

通过该结果,我可以选择每个id条目,通过它在状态字段上执行SUM的检查结果进行限制,如果SUM结果与检查结果相等,则检查点是连续的,如:

WITH tbl AS (
    SELECT id, status, SUM(status) AS "sum"
    FROM (
            SELECT id, status FROM example_table WHERE id = 1 ORDER BY date LIMIT 4
        ) AS tbl2
    GROUP BY
            status,id
)
SELECT
    id,"sum"
FROM
    tbl
WHERE
    status = 1

获得以下结果:

id | sum
1  | 4

由于总和结果等于检查第一个查询,我可以确定检查点是连续的。但这次以id 2为例,它的查询是:

WITH tbl AS (
    SELECT id, status, SUM(status) AS "sum"
    FROM (
            SELECT id, status FROM example_table WHERE id = 2 ORDER BY date LIMIT 3
        ) AS tbl2
    GROUP BY
            status,id
)
SELECT
    id,"sum"
FROM
    tbl
WHERE
    status = 1

请注意,我根据我正在使用的ID及其在第一个查询中的检查结果更改了WHERE上的id和LIMIT值,并得到以下结果:

id | sum
2  | 2

由于该查询中id 2的sum字段值与其检查值不同,我可以说它不是连续的。每个id都可以重复这种模式。

正如我之前所说,要以这种方式解决问题,我需要通过代码来完成,但在特定情况下,我需要它在SQL中。

我还发现了以下文章:

postgres detect repeating patterns of zeros

问题类似于我的问题,但他想检测重复的零,它有点启发我,但还不足以解决我自己的问题。

提前致谢!

1 个答案:

答案 0 :(得分:1)

您正在寻找的模式是错过的检查点,然后是完成的检查点。使用下一个(按时间戳)检查点加入用户的每个检查点,然后查找状态0加入状态1。

以下是一个例子:

create table tab (id int,date date,status int);
insert into tab values(1  , '2014-01-10' , 1),(1  , '2014-02-10' , 1),(1  , '2014-03-10' , 1),(1  , '2014-04-10' , 1),(1  , '2014-05-10' , 0),(1  , '2014-06-10' , 0),(2  , '2014-01-10' , 1),(2  , '2014-02-10' , 1),(2  , '2014-03-10' , 0),(2  , '2014-04-10' , 1),(2  , '2014-05-10' , 0),(2  , '2014-06-10' , 0),(3  , '2014-01-10' , 1),(3  , '2014-02-10' , 0),(3  , '2014-03-10' , 0),(3  , '2014-04-10' , 1),(3  , '2014-05-10' , 0),(3  , '2014-06-10' , 0),(4  , '2014-01-10' , 0),(4  , '2014-02-10' , 1),(4  , '2014-03-10' , 1),(4  , '2014-04-10' , 1),(4  , '2014-05-10' , 0),(4  , '2014-06-10' , 0),(5  , '2014-01-10' , 0),(5  , '2014-02-10' , 1),(5  , '2014-03-10' , 0),(5  , '2014-04-10' , 1),(5  , '2014-05-10' , 0),(5  , '2014-06-10' , 0);
with tabwithrow as
    (select *
           , row_number() OVER(PARTITION by id order by date) rnum
        from    tab)
select  *
from    tabwithrow a
join    tabwithrow b on b.rnum = a.rnum + 1
        and a.id = b.id 
        and a.status = 0 
        and b.status = 1;