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

過去10日間の行を取得する

    これは疑わしくないように見えますが、非常に難しいです。 。

    前提条件

    crosstab() クエリ

    最高のパフォーマンスと短いクエリ文字列を取得するには(特にこのクエリを頻繁に実行する場合)、追加のモジュール tablefunc さまざまなcrosstab()を提供する 機能。 基本的な手順:

    基本的なクエリ

    最初にこれらを正しく取得する必要があります。

    過去10日間:

    SELECT DISTINCT date
    FROM   book
    WHERE  sid = 1
    ORDER  BY date DESC
    LIMIT  10;
    

    ウィンドウ関数 density_rank()

    SELECT *
    FROM  (
       SELECT name
            , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
            , count
       FROM   book
       WHERE  sid = 1
       ) sub
    WHERE  date_rnk < 11
    ORDER  BY name, date_rnk DESC;
    

    (このクエリには実際の日付は含まれません。)

    出力列の列名(完全なソリューションの場合):

    SELECT 'bookname, "' || string_agg(to_char(date, 'DD/MM/YYYY'), '", "' ORDER BY date) || '"'
    FROM  (
       SELECT DISTINCT date
       FROM   book
       WHERE  sid = 1
       ORDER  BY date DESC
       LIMIT  10
       ) sub;
    

    静的な列名を使用した単純な結果

    これで十分かもしれませんが、結果には実際の日付は表示されません:

    SELECT * FROM crosstab(
      'SELECT *
       FROM  (
          SELECT name
               , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
               , count
          FROM   book
          WHERE  sid = 1
          ) sub
       WHERE  date_rnk < 11
       ORDER  BY name, date_rnk DESC'
    , 'SELECT generate_series(10, 1, -1)'
     ) AS (bookname text
         , date1 int, date2 int, date3 int, date4 int, date5 int
         , date6 int, date7 int, date8 int, date9 int, date10 int);
    

    繰り返し使用する場合は、少し単純化するために、この(非常に高速な)汎用C関数を10個の整数列に対して1回作成することをお勧めします。

    CREATE OR REPLACE FUNCTION crosstab_int10(text, text)
      RETURNS TABLE (bookname text
                   , date1 int, date2 int, date3 int, date4 int, date5 int
                   , date6 int, date7 int, date8 int, date9 int, date10 int)
      LANGUAGE C STABLE STRICT AS
    '$libdir/tablefunc','crosstab_hash';
    

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

    次に、通話は次のようになります。

    SELECT * FROM crosstab(
      'SELECT *
       FROM  (
          SELECT name
               , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
               , count
          FROM   book
          WHERE  sid = 1
          ) sub
       WHERE  date_rnk < 11
       ORDER  BY name, date_rnk DESC'
    , 'SELECT generate_series(10, 1, -1)'
     );  -- no column definition list required!
    

    動的な列名を使用した完全なソリューション

    実際の質問はもっと複雑で、動的な列名も必要です。
    特定のテーブルの場合、結果のクエリは次のようになります。

    SELECT * FROM crosstab_int10(
      'SELECT *
       FROM  (
          SELECT name
               , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
               , count
          FROM   book
          WHERE  sid = 1
          ) sub
       WHERE  date_rnk < 11
       ORDER  BY name, date_rnk DESC'
    , 'SELECT generate_series(10, 1, -1)'
       ) AS t(bookname
            , "04/11/2015", "05/11/2015", "06/11/2015", "07/11/2015", "08/11/2015"
            , "09/11/2015", "10/11/2015", "11/11/2015", "15/11/2015", "17/11/2015");

    難しいのは、動的な列名を抽出することです。クエリ文字列を手動でアセンブルするか、(むしろ)この関数に任せてください:

    CREATE OR REPLACE FUNCTION f_generate_date10_sql(_sid int = 1) 
      RETURNS text
      LANGUAGE sql AS
    $func$
    SELECT format(
     $$SELECT * FROM crosstab_int10(
      'SELECT *
       FROM  (
          SELECT name
               , dense_rank() OVER (ORDER BY date DESC) AS date_rnk
               , count
          FROM   book
          WHERE  sid = %1$s
          ) sub
       WHERE  date_rnk < 11
       ORDER  BY name, date_rnk DESC'
    , 'SELECT generate_series(10, 1, -1)'
       ) AS ct(bookname, "$$
    || string_agg(to_char(date, 'DD/MM/YYYY'), '", "' ORDER BY date) || '")'
     , _sid)
    FROM  (
       SELECT DISTINCT date
       FROM   book
       WHERE  sid = 1
       ORDER  BY date DESC
       LIMIT  10
       ) sub
    $func$;
    

    電話:

    SELECT f_generate_date10_sql(1);
    

    これにより、目的のクエリが生成されます 、順番に実行します。

    db <> fiddle こちら




    1. mysqlで時間間隔を取得する

    2. UNHEXとXの違い(MySQL)

    3. PostgreSQLとMySQLの違いは何ですか?

    4. SUBSTRING()関数を使用してSQLServerの文字列から部分文字列を返す方法