在多个参数和条件下检测SQL岛

时间:2012-09-17 21:46:04

标签: sql postgresql gaps-and-islands

(PostgreSQL 8.4)我对SQL gap-and-islands here on Stack Overflow进行了很好的介绍,但我还有一个问题。许多岛检测CTE基于时间戳的运行顺序和一些在其改变时打破序列的标志。但是,如果“休息”条件稍微复杂一点怎么办呢?

CREATE TABLE T1
(
  id SERIAL PRIMARY KEY,
  val INT,   -- some device
  status INT -- 0=OFF, 1=ON
);

INSERT INTO T1 (val, status) VALUES (10, 1);
INSERT INTO T1 (val, status) VALUES (10, 0);
INSERT INTO T1 (val, status) VALUES (11, 1);
INSERT INTO T1 (val, status) VALUES (11, 1);
INSERT INTO T1 (val, status) VALUES (10, 0);
INSERT INTO T1 (val, status) VALUES (12, 1);
INSERT INTO T1 (val, status) VALUES (13, 1);
INSERT INTO T1 (val, status) VALUES (13, 0);
INSERT INTO T1 (val, status) VALUES (13, 1);

在这种情况下,val代表设备,status代表ONOFF。我想使用以下逻辑选择记录13679

  1. 10打开 - 确定,新序列,包含在SELECT

  2. 10关闭 - 正确结束序列,忽略行

  3. 11打开 - 确定,新序列,包含在SELECT

  4. 11开启 - 复制,忽略行

  5. 10关闭 - #10未开启,忽略

  6. 12打开 - 确定,隐式关闭#11,包括在SELECT中

  7. 13打开 - 确定,隐式关闭#12,包含在SELECT中

  8. 13关闭 - 正确结束序列,忽略行

  9. 13打开 - 确定,新序列,包含在SELECT

  10. 基本上,一次只能打开一个设备,“中断”条件是:

    • new.val = running.val AND new.status = 0
    • new.val<> running.val和new.status = 1

    我正在寻找CTE形式的东西,没有游标

1 个答案:

答案 0 :(得分:2)

回答更新的问题

SELECT *
FROM  (
   SELECT *
         ,lag(val, 1, 0) OVER (PARTITION BY status ORDER BY id) last_val
         ,lag(status) OVER (PARTITION BY val ORDER BY id) last_status
   FROM   t1
   ) x
WHERE  status = 1
AND    (last_val <> val OR last_status = 0)

如何?

与之前相同,但这次结合了两个窗口函数。打开设备符合以下条件:
1.打开的最后一个设备是不同的 2.或者同一设备已在其最后一个条目中切换 off 。对于分区的第一行,NULL的角落案例无关紧要,因为该行已经在 1中合格。


回答原始版本的问题。

如果您理解正确的任务,这个简单的查询可以完成工作:

SELECT *
FROM  (
   SELECT *
         ,lag(val, 1, 0) OVER (ORDER BY id) last_on
   FROM   t1
   WHERE  status = 1
   ) x
WHERE  last_on <> val

按要求返回第1,3,6,7行。

如何?

根据你的描述,子查询忽略所有关闭,因为这只是噪音。留下设备开启的条目。其中,只有那些条目被取消资格,同一设备已经开启(最后一个条目开启)。使用window function lag()即可。特别是我提供0作为默认值来涵盖第一行的特殊情况 - 假设没有设备val = 0
如果有,请选择另一个不可能的数字 如果无法编号,请将特殊情况保留为NULL lag(val) OVER ...,并在外部查询中查看:

WHERE last_on IS DISTINCT FROM val