これは疑わしくないように見えますが、非常に難しいです。 。
前提条件
- カウントは
integer
。 - テーブルブックのすべての列が定義されています
NOTNULL
。 -
複合
(name、sid、date)
テーブルbook
で一意です 。UNIQUE
が必要です 制約、できれば(パフォーマンスのために) thisの列 注文:UNIQUE(sid, date, name)
これにより、パフォーマンスに必要なインデックスが自動的に提供されます。 (それ以外の場合は作成します。)参照:
crosstab()
クエリ
最高のパフォーマンスと短いクエリ文字列を取得するには(特にこのクエリを頻繁に実行する場合)、追加のモジュール tablefunc
さまざまなcrosstab()
を提供する 機能。 基本的な手順:
基本的なクエリ
最初にこれらを正しく取得する必要があります。
過去10日間:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
ウィンドウ関数density_rank()を使用した過去10日間の数値コード>
:
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 こちら