3

(PostgreSQL 8.4)我在 Stack Overflow 上对 SQL gaps-and-islands 有了很好的介绍,但我仍然有一个问题。许多孤岛检测 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表示一个设备,并且statusONOFF。我想使用以下逻辑选择记录1、、、3679

  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 中

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

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

我正在寻找 CTE 形式的东西,请不要使用光标

4

1 回答 1

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. 或同一设备在其最后一次进入时已被关闭。分区第一行的极端情况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 行。

如何?

根据您的描述,子查询忽略所有关闭,因为这只是噪音。留下打开设备的条目。其中,只有那些条目被取消资格,其中相同的设备已经打开(最后一个条目打开)。为此使用窗口函数lag()。特别是我0默认提供覆盖第一行的特殊情况 - 假设没有设备带有val = 0.
如果有,请选择另一个不可能的数字。
如果没有数字是不可能的,则将特殊情况保留为NULLwithlag(val) OVER ...并在外部查询中检查:

WHERE last_on IS DISTINCT FROM val
于 2012-09-17T22:08:11.830 回答