sql >> データベース >  >> RDS >> PostgreSQL

連続した繰り返し/重複の順序付きカウント

    テストケース

    まず、 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を使用 関数内で、これは現在のトランザクションに対してのみ有効です。関数にコメント行を追加しました。 しない これはサーバーを破壊する可能性があるため、グローバルに非常に高く設定します。マニュアルを読んでください。




    1. ORDERBYとGROUPBYを一緒に使用する

    2. 非同期スレーブからMySQLGaleraクラスターを回復する方法

    3. エラー:ファイルXX.csvを統計できませんでした:不明なエラー

    4. PostgreSQLでデータベースのコピーを作成する