Ini terlihat tidak mencurigakan, tapi ini pertanyaan yang sangat buruk .
Asumsi
- Jumlah Anda adalah
integer
. - Semua kolom dalam table book didefinisikan
NOT NULL
. -
Gabungan
(name, sid, date)
unik dalam tabelbook
. Anda harus memilikiUNIQUE
kendala, lebih disukai (untuk kinerja) dengan kolom di ini pesanan:UNIQUE(sid, date, name)
Ini memberikan indeks yang diperlukan untuk kinerja secara otomatis. (Jika tidak, buat satu.) Lihat:
crosstab()
kueri
Untuk mendapatkan kinerja terbaik dan string kueri pendek (terutama jika Anda sering menjalankan kueri ini) saya sarankan modul tambahan tablefunc
menyediakan berbagai crosstab()
fungsi. Petunjuk dasar:
Kueri dasar
Anda harus melakukannya dengan benar terlebih dahulu.
10 hari terakhir:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
Angka selama 10 hari terakhir menggunakan fungsi jendela dense_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;
(Tidak termasuk tanggal aktual dalam kueri ini.)
Nama kolom untuk kolom keluaran (untuk solusi lengkap):
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;
Hasil sederhana dengan nama kolom statis
Ini mungkin cukup baik untuk Anda - tetapi kami tidak melihat tanggal sebenarnya dalam hasil:
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);
Untuk penggunaan berulang, saya sarankan Anda membuat fungsi C generik (sangat cepat) ini untuk 10 kolom bilangan bulat sekali, untuk sedikit menyederhanakan:
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';
Detail dalam jawaban terkait ini:
Kemudian panggilan Anda menjadi:
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!
Solusi lengkap dengan nama kolom dinamis
Pertanyaan Anda yang sebenarnya lebih rumit, Anda juga menginginkan nama kolom yang dinamis.
Untuk tabel tertentu, kueri yang dihasilkan dapat terlihat seperti ini:
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");
Kesulitannya adalah menyaring nama kolom dinamis. Baik merakit string kueri dengan tangan, atau (lebih tepatnya) biarkan fungsi ini melakukannya untuk Anda:
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$;
Telepon:
SELECT f_generate_date10_sql(1);
Ini menghasilkan kueri yang diinginkan , yang Anda jalankan secara bergantian.
db<>fiddle di sini