1

(PostgreSQL 8.4)前の例を続けて、ウィンドウ関数を使用したギャップとアイランドの処理についてさらに理解を深めたいと思います。次の表とデータを検討してください。

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

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

前に説明したように、デバイスはオンとオフを切り替えます。今回は特定のシーケンスを抽出したいと思います。

  • 重複していないすべての新しいONステータスレコードを表示します(同じデバイスを2回続けて表示します)
  • 現在のデバイスから適切なOFFステータスを表示するON

私が得ることができる最も近いものはこれです:

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

これにより、サンプルに含まれていないより多くの偽のデータが除外されますが、基本的には、後続の重複(ステータスに関係なく)とOFF一致しない上位レコードを削除することです。レコード、、、3が返されますが4、5番目は必要ありません。新しいの後に来たものです。そのため、そのギャップを飛び越えて、現在アクティブなデバイスに適した次のデバイスを探す必要があります。56OFFONOFF

  1. 10がオフになります-このコンテキストでは偽物ですが、lag()を台無しにします
  2. 11がオフになります-このコンテキストでは偽物ですが、lag()を台無しにします
  3. 11がオンになります-OK、新しいシーケンス、SELECTに含めます
  4. 10がオンになります-OK、新しいシーケンス、SELECTに含める
  5. 11がオフになります-メッセージが遅れて届きました。ギャップを無視する必要があります
  6. 10がオフになります-OK、行4に対して適切なオフ、SELECTに含める必要があります

適切にフィルタリングされたら、その上で次の行のID(タイムスタンプを想像してください)を取得し、ステータスlead()ではないすべてのレコードを除外したいと思います。ONこれには3つの埋め込みSELECTステートメントが必要になると思います。ONこれにより、別の状態または適切な回転の状態になるまで、デバイスがアクティブであった期間を明確に理解できますOFF

4

1 に答える 1

3

ウィンドウ関数を使用したクエリ

SELECT *
FROM  (
   SELECT *
         ,lag(val, 1, 0)    OVER (PARTITION BY status ORDER BY id) AS last_val
         ,lag(status, 1, 0) OVER w2 AS last_status
         ,lag(next_id)      OVER w2 AS next_id_of_last_status
   FROM  (
      SELECT *, lead(id) OVER (PARTITION BY status ORDER BY id) AS next_id
      FROM   t1
      ) AS t
   WINDOW w2 AS (PARTITION BY val ORDER BY id)
  ) x
WHERE (last_val <> val OR last_status <> status)
AND   (status = 1 
       OR last_status = 1
          AND ((next_id_of_last_status > id) OR next_id_of_last_status IS NULL)
      )
ORDER  BY id

すでに持っていたものに加えて、有効なオフスイッチが必要です。

デバイスが()の前に切り替えられ、その後の次の操作が問題の切り替え()の後にOFF行われる場合に有効なスイッチ。ONlast_status = 1ONOFFnext_id_of_last_status > id

最後の操作があったという特殊なケースを用意する必要があるので、さらに( )ONをチェックします。NULLOR next_id_of_last_status IS NULL

next_id_of_last_status、私たちが取ったのと同じウィンドウから来ていますlast_status。したがって、明示的なウィンドウ宣言に追加の構文を導入したので、繰り返す必要はありません。

WINDOW w2 AS (PARTITION BY val ORDER BY id)

また、以前のサブクエリ(subquery t)の最後のステータスの次のIDを取得する必要があります。

それをすべて理解していればlead()、最終的な目的地にたどり着くために、このクエリの上で問題なく叩くことができるはずです。:)

PL/pgSQL関数

これが複雑になったら、手続き型処理に切り替えます。

この比較的単純なplpgsql関数は、テーブル全体を1回だけスキャンする必要があるという単純な理由から、複雑なウィンドウ関数クエリのパフォーマンスを低下させます。

CREATE OR REPLACE FUNCTION valid_t1 (OUT t t1)  -- row variable of table type
  RETURNS SETOF t1 LANGUAGE plpgsql AS
$func$
DECLARE
   _last_on int := -1;  -- init with impossible value
BEGIN

FOR t IN
   SELECT * FROM t1 ORDER BY id
LOOP
   IF t.status = 1 THEN
      IF _last_on <> t.val THEN
         RETURN NEXT;
         _last_on := t.val;
      END IF;
   ELSE
      IF _last_on = t.val THEN
         RETURN NEXT;
         _last_on := -1;
      END IF;
   END IF;
END LOOP;

END
$func$;

電話:

SELECT * FROM valid_t1();
于 2012-09-19T20:57:19.360 に答える