Das sieht unverdächtig aus, ist aber eine höllische Frage .
Annahmen
- Ihre Zählwerte sind
integer
. - Alle Spalten im Tabellenbuch sind
NOT NULL
definiert . -
Der zusammengesetzte
(name, sid, date)
ist in der Tabellebook
eindeutig . Sie sollten einenUNIQUE
haben Einschränkung, vorzugsweise (aus Performancegründen) mit Spalten in this Bestellung:UNIQUE(sid, date, name)
Dadurch wird der für die Performance benötigte Index automatisch bereitgestellt. (Erstellen Sie andernfalls eine.) Siehe:
crosstab()
Abfragen
Um Top-Performance und kurze Query-Strings zu erhalten (insbesondere wenn Sie diese Query häufig ausführen), empfehle ich das zusätzliche Modul tablefunc
Bereitstellen verschiedener crosstab()
Funktionen. Grundlegende Anweisungen:
Grundlegende Abfragen
Sie müssen diese zuerst richtig machen.
Die letzten 10 Tage:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
Zahlen für die letzten 10 Tage mit der Fensterfunktion 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;
(Echte Daten werden in dieser Abfrage nicht berücksichtigt.)
Spaltennamen für Ausgabespalten (für vollständige Lösung):
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;
Einfaches Ergebnis mit statischen Spaltennamen
Dies mag für Sie ausreichen - aber wir sehen keine tatsächlichen Daten im Ergebnis:
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);
Zur wiederholten Verwendung schlage ich vor, dass Sie diese (sehr schnelle) generische C-Funktion für 10 Integer-Spalten einmal erstellen, um die Dinge ein wenig zu vereinfachen:
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';
Details in dieser verwandten Antwort:
Dann wird Ihr Aufruf zu:
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!
Vollständige Lösung mit dynamischen Spaltennamen
Ihre eigentliche Frage ist komplizierter, Sie möchten auch dynamische Spaltennamen.
Für eine gegebene Tabelle könnte die resultierende Abfrage dann so aussehen:
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");
Die Schwierigkeit besteht darin, dynamische Spaltennamen zu destillieren. Bauen Sie die Abfragezeichenfolge entweder von Hand zusammen oder lassen Sie (viel besser) diese Funktion für Sie erledigen:
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$;
Aufruf:
SELECT f_generate_date10_sql(1);
Dies erzeugt die gewünschte Abfrage , die Sie wiederum ausführen.
db<>fiddle hier