テストケース
まず、 sqlfiddle でデータを表示するためのより便利な方法、またはさらに優れた方法 、遊ぶ準備ができています:
CREATE TEMP TABLE data(
system_measured int
, time_of_measurement int
, measurement int
);
INSERT INTO data VALUES
(1, 1, 5)
,(1, 2, 150)
,(1, 3, 5)
,(1, 4, 5)
,(2, 1, 5)
,(2, 2, 5)
,(2, 3, 5)
,(2, 4, 5)
,(2, 5, 150)
,(2, 6, 5)
,(2, 7, 5)
,(2, 8, 5);
簡略化されたクエリ
不明な点があるため、上記のとおりと想定しています。
次に、クエリを簡略化して次のようにします。
WITH x AS (
SELECT *, CASE WHEN lag(measurement) OVER (PARTITION BY system_measured
ORDER BY time_of_measurement) = measurement
THEN 0 ELSE 1 END AS step
FROM data
)
, y AS (
SELECT *, sum(step) OVER(PARTITION BY system_measured
ORDER BY time_of_measurement) AS grp
FROM x
)
SELECT * ,row_number() OVER (PARTITION BY system_measured, grp
ORDER BY time_of_measurement) - 1 AS repeat_ct
FROM y
ORDER BY system_measured, time_of_measurement;
さて、純粋なSQLを使用することはすべて素晴らしくて光沢がありますが、これははるかに plpgsql関数を使用すると、このクエリで少なくとも3回のスキャンが必要な単一のテーブルスキャンで実行できるため、より高速になります。
plpgsql関数を使用した高速化:
CREATE OR REPLACE FUNCTION x.f_repeat_ct()
RETURNS TABLE (
system_measured int
, time_of_measurement int
, measurement int, repeat_ct int
) LANGUAGE plpgsql AS
$func$
DECLARE
r data; -- table name serves as record type
r0 data;
BEGIN
-- SET LOCAL work_mem = '1000 MB'; -- uncomment an adapt if needed, see below!
repeat_ct := 0; -- init
FOR r IN
SELECT * FROM data d ORDER BY d.system_measured, d.time_of_measurement
LOOP
IF r.system_measured = r0.system_measured
AND r.measurement = r0.measurement THEN
repeat_ct := repeat_ct + 1; -- start new array
ELSE
repeat_ct := 0; -- start new count
END IF;
RETURN QUERY SELECT r.*, repeat_ct;
r0 := r; -- remember last row
END LOOP;
END
$func$;
電話:
SELECT * FROM x.f_repeat_ct();
この種のplpgsql関数では、常に列名をテーブル修飾してください。修飾されていない場合に優先される出力パラメータと同じ名前を使用するためです。
数十億行
数十億の場合 行の 、この操作を分割することをお勧めします。ここでマニュアルを引用します:
注:
RETURN NEXT
の現在の実装 およびRETURN QUERY
上記で説明したように、関数から戻る前に結果セット全体を格納します。つまり、PL / pgSQL関数が非常に大きな結果セットを生成する場合、パフォーマンスが低下する可能性があります。メモリの枯渇を避けるためにデータはディスクに書き込まれますが、結果セット全体が生成されるまで関数自体は返されません。 PL / pgSQLの将来のバージョンでは、ユーザーがこの制限のない集合戻り関数を定義できるようになる可能性があります。現在、データがディスクに書き込まれ始めるポイントは、work_memconfiguration変数によって制御されます。より大きな結果セットをメモリに保存するのに十分なメモリがある管理者は、このパラメータを増やすことを検討する必要があります。
一度に1つのシステムの行を計算するか、work_mem
に十分高い値を設定することを検討してください。 負荷に対処するために。 work_memの詳細については、見積もりに記載されているリンクをたどってください。
1つの方法は、work_mem
に非常に高い値を設定することです。 SET LOCAL
を使用 関数内で、これは現在のトランザクションに対してのみ有効です。関数にコメント行を追加しました。 しない これはサーバーを破壊する可能性があるため、グローバルに非常に高く設定します。マニュアルを読んでください。