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

グループごとに最初と最後の行から値を取得します

    さまざまな簡単で高速な方法があります。

    2x DISTINCT ON

    SELECT *
    FROM  (
       SELECT DISTINCT ON (name)
              name, week AS first_week, value AS first_val
       FROM   tbl
       ORDER  BY name, week
       ) f
    JOIN (
       SELECT DISTINCT ON (name)
              name, week AS last_week, value AS last_val
       FROM   tbl
       ORDER  BY name, week DESC
       ) l USING (name);
    

    またはそれより短い:

    SELECT *
    FROM  (SELECT DISTINCT ON (1) name, week AS first_week, value AS first_val FROM tbl ORDER BY 1,2) f
    JOIN  (SELECT DISTINCT ON (1) name, week AS last_week , value AS last_val  FROM tbl ORDER BY 1,2 DESC) l USING (name);
    

    シンプルでわかりやすい。私の古いテストでも最速です。 DISTINCT ONの詳細な説明 :

    • 各GROUPBYグループの最初の行を選択しますか?

    2xウィンドウ関数、1x DISTINCT ON

    SELECT DISTINCT ON (name)
           name, week AS first_week, value AS first_val
         , first_value(week)  OVER w AS last_week
         , first_value(value) OVER w AS last_value
    FROM   tbl t
    WINDOW w AS (PARTITION BY name ORDER BY week DESC)
    ORDER  BY name, week;
    

    明示的なWINDOW 句はコードを短縮するだけで、パフォーマンスには影響しません。

    first_value() 複合型の

    集計関数min() またはmax() 複合タイプを入力として受け入れないでください。カスタム集計関数を作成する必要があります(それほど難しくはありません)。
    ただし、ウィンドウ関数は first_value() およびlast_value() 行う 。その上で、簡単なソリューションを考案できます:

    簡単なクエリ

    SELECT DISTINCT ON (name)
           name, week AS first_week, value AS first_value
         ,(first_value((week, value)) OVER (PARTITION BY name ORDER BY week DESC))::text AS l
    FROM   tbl t
    ORDER  BY name, week;
    

    出力にはすべてのデータが含まれますが、先週の値は匿名レコードに詰め込まれます(オプションで text にキャストされます) )。分解された値が必要になる場合があります。

    テーブルタイプの日和見的な使用による分解結果

    そのためには、よく知られている複合型が必要です。適合したテーブル定義により、テーブルタイプ自体を日和見的に直接使用できるようになります。

    CREATE TABLE tbl (week int, value int, name text);  -- optimized column order
    

    およびvalue 最初に来るので、テーブルタイプ自体で並べ替えることができます:

    SELECT (l).name, first_week, first_val
         , (l).week AS last_week, (l).value AS last_val
    FROM  (
       SELECT DISTINCT ON (name)
              week AS first_week, value AS first_val
            , first_value(t) OVER (PARTITION BY name ORDER BY week DESC) AS l
       FROM   tbl t
       ORDER  BY name, week
       ) sub;
    

    ユーザー定義の行タイプからの分解結果

    ほとんどの場合、それはおそらく不可能です。複合型をCREATETYPEに登録します (永続的)または CREATE TEMP TABLE (セッション期間中):

    CREATE TEMP TABLE nv(last_week int, last_val int);  -- register composite type
    
    SELECT name, first_week, first_val, (l).last_week, (l).last_val
    FROM (
       SELECT DISTINCT ON (name)
              name, week AS first_week, value AS first_val
            , first_value((week, value)::nv) OVER (PARTITION BY name ORDER BY week DESC) AS l
       FROM   tbl t
       ORDER  BY name, week
       ) sub;
    

    カスタム集計関数first() last()

    データベースごとに1回関数と集計を作成します:

    CREATE OR REPLACE FUNCTION public.first_agg (anyelement, anyelement)
      RETURNS anyelement
      LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
    'SELECT $1;'
    
    CREATE AGGREGATE public.first(anyelement) (
      SFUNC = public.first_agg
    , STYPE = anyelement
    , PARALLEL = safe
    );
    
    
    CREATE OR REPLACE FUNCTION public.last_agg (anyelement, anyelement)
      RETURNS anyelement
      LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE AS
    'SELECT $2';
    
    CREATE AGGREGATE public.last(anyelement) (
      SFUNC = public.last_agg
    , STYPE = anyelement
    , PARALLEL = safe
    );
    

    次に:

    SELECT name
         , first(week) AS first_week, first(value) AS first_val
         , last(week)  AS last_week , last(value)  AS last_val
    FROM  (SELECT * FROM tbl ORDER BY name, week) t
    GROUP  BY name;
    

    おそらく最もエレガントなソリューションです。追加のモジュールfirst_last_aggで高速化 C実装を提供します。
    PostgresWikiの手順を比較してください。

    関連:

    • 各インフルエンサーのフォロワーの成長を経時的に計算する

    db<>ここでフィドル (すべて表示)
    古いsqlfiddle

    これらの各クエリは、 EXPLAIN ANALYZEを使用した5万行のテーブルでのクイックテストで現在受け入れられている回答よりも大幅に高速でした。 。

    他にも方法があります。データの分散によっては、さまざまなクエリスタイルの方が(はるかに)高速になる場合があります。参照:

    • GROUP BYクエリを最適化して、ユーザーごとに最新の行を取得します


    1. コマンドプロンプトを使用してmysqlデータベースをエクスポートする方法は?

    2. ORACLE 11gのテーブル値関数? (パラメータ化されたビュー)

    3. OracleSequencenextvalは数を前後にジャンプしています

    4. 大規模データベース管理システム:設計および設計者