Dit ziet er niet verdacht uit, maar het is een hele vraag .
Aannames
- Uw tellingen zijn
integer
. - Alle kolommen in het tabelboek zijn gedefinieerd
NOT NULL
. -
De samengestelde
(name, sid, date)
is uniek in tabelbook
. Je zou eenUNIQUE
. moeten hebben beperking, bij voorkeur (voor prestaties) met kolommen in dit bestelling:UNIQUE(sid, date, name)
Dit levert automatisch de index die nodig is voor prestaties. (Maak er anders een.) Zie:
crosstab()
vragen
Om topprestaties en korte queryreeksen te krijgen (vooral als u deze query vaak uitvoert), raad ik de extra module tablefunc
het verstrekken van verschillende crosstab()
functies. Basisinstructies:
Basisvragen
Je moet deze eerst goed krijgen.
De afgelopen 10 dagen:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
Nummers van de afgelopen 10 dagen met behulp van de vensterfunctie 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;
(Exclusief de werkelijke datums in deze zoekopdracht.)
Kolomnamen voor uitvoerkolommen (voor volledige oplossing):
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;
Eenvoudig resultaat met statische kolomnamen
Dit is misschien goed genoeg voor u, maar we zien geen echte datums in het resultaat:
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);
Voor herhaald gebruik raad ik aan om deze (zeer snelle) generieke C-functie voor 10 integer-kolommen een keer te maken, om de zaken een beetje te vereenvoudigen:
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 dit gerelateerde antwoord:
Dan wordt uw oproep:
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!
Volledige oplossing met dynamische kolomnamen
Uw eigenlijke vraag is ingewikkelder, u wilt ook dynamische kolomnamen.
Voor een bepaalde tabel kan de resulterende query er dan als volgt uitzien:
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");
De moeilijkheid is om dynamische kolomnamen te distilleren. Stel de queryreeks met de hand samen, of (veel liever) laat deze functie het voor u doen:
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$;
Bel:
SELECT f_generate_date10_sql(1);
Dit genereert de gewenste zoekopdracht , die u beurtelings uitvoert.
db<>fiddle hier