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

最小のCOUNT()のみで行と列を転置しますか(別名ピボット)?

    CASE

    ケースが示されているほど単純な場合は、CASE ステートメントは次のようになります:

    SELECT year
         , sum(CASE WHEN animal = 'kittens' THEN price END) AS kittens
         , sum(CASE WHEN animal = 'puppies' THEN price END) AS puppies
    FROM  (
       SELECT year, animal, avg(price) AS price
       FROM   tab_test
       GROUP  BY year, animal
       HAVING count(*) > 2
       ) t
    GROUP  BY year
    ORDER  BY year;
    

    sum()を使用するかどうかは関係ありません 、max() またはmin() 外部クエリの集計関数として。この場合、これらはすべて同じ値になります。

    SQLフィドル

    crosstab()

    カテゴリが多いほど、crosstab()を使用すると簡単になります クエリ。これは、大きなテーブルの場合も高速である必要があります

    追加のモジュールtablefunc をインストールする必要があります (データベースごとに1回)。 Postgres 9.1以降、これは次のように簡単です。

    CREATE EXTENSION tablefunc;
    

    この関連する回答の詳細:

    SELECT * FROM crosstab(
          'SELECT year, animal, avg(price) AS price
           FROM   tab_test
           GROUP  BY animal, year
           HAVING count(*) > 2
           ORDER  BY 1,2'
    
          ,$$VALUES ('kittens'::text), ('puppies')$$)
    AS ct ("year" text, "kittens" numeric, "puppies" numeric);
    

    サイトでは追加のモジュールが許可されていないため、これにはsqlfiddleはありません。

    ベンチマーク

    私の主張を検証するために、小さなテストデータベースの実際のデータに近いものを使用して簡単なベンチマークを実行しました。 PostgreSQL9.1.6。 EXPLAIN ANALYZEでテストする 、ベスト10:

    10020行のテストセットアップ:

    CREATE TABLE tab_test (year int, animal text, price numeric);
    
    -- years with lots of rows
    INSERT INTO tab_test
    SELECT 2000 + ((g + random() * 300))::int/1000 
         , CASE WHEN (g + (random() * 1.5)::int) %2 = 0 THEN 'kittens' ELSE 'puppies' END
         , (random() * 200)::numeric
    FROM   generate_series(1,10000) g;
    
    -- .. and some years with only few rows to include cases with count < 3
    INSERT INTO tab_test
    SELECT 2010 + ((g + random() * 10))::int/2
         , CASE WHEN (g + (random() * 1.5)::int) %2 = 0 THEN 'kittens' ELSE 'puppies' END
         , (random() * 200)::numeric
    FROM   generate_series(1,20) g;
    

    結果:

    @bluefeet
    合計実行時間:95.401ミリ秒

    @wildplasser (異なる結果、count <= 3の行が含まれます )
    合計実行時間:64.497ミリ秒

    @Andreiy (+ ORDER BY
    &@ Erwin1-CASE (どちらもほぼ同じパフォーマンスです)
    合計実行時間:39.105ミリ秒

    @ Erwin2-crosstab()
    合計実行時間:17.644ミリ秒

    わずか20行で、大部分が比例する(ただし無関係な)結果。 @wildplasserのCTEだけが、より多くのオーバーヘッドと少しのスパイクを持っています。

    一握り以上の行がある場合、crosstab() @Andreiyのクエリは、私の簡略化されたバージョンとほぼ同じように実行され、外部のSELECTの集計関数になります。 (min()max()sum() )測定可能な違いはありません(グループごとに2行のみ)。

    すべてが期待どおりで、驚くことではありません。セットアップを行って、@homeで試してください。



    1. mysqlテーブルから最新の日付レコードのセットを選択する方法

    2. Html/PhpフォームがSQLデータベースに追加されない

    3. 動的ピボットテーブルの列mysql

    4. PostgreSQLでデッドロックをシミュレートする方法は?