Det här ser inte misstänkt ut, men det är en jävla fråga .
Antaganden
- Dina siffror är
heltal
. - Alla kolumner i tabellboken är definierade
NOT NULL
. -
Den sammansatta
(namn, sid, datum)
är unik i tabellenbok
. Du bör ha enUNIQUE
begränsning, helst (för prestanda) med kolumner i denna beställning:UNIQUE(sid, date, name)
Detta ger automatiskt det index som behövs för prestanda. (Skapa annars en.) Se:
crosstab()
frågor
För att få högsta prestanda och korta frågesträngar (särskilt om du kör den här frågan ofta) föreslår jag tilläggsmodulen tablefunc
tillhandahåller olika crosstab()
funktioner. Grundläggande instruktioner:
Grundläggande frågor
Du måste få dessa rätt först.
De senaste 10 dagarna:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
Siffror för de senaste 10 dagarna med fönsterfunktionen 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;
(Inklusive faktiska datum i denna fråga.)
Kolumnnamn för utdatakolumner (för fullständig lösning):
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;
Enkelt resultat med statiska kolumnnamn
Detta kan vara tillräckligt bra för dig - men vi ser inga faktiska datum i resultatet:
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);
För upprepad användning föreslår jag att du skapar denna (mycket snabba) generiska C-funktion för 10 heltalskolumner en gång, för att förenkla saker och ting lite:
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';
Detaljer i detta relaterade svar:
Då blir ditt samtal:
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!
Fullständig lösning med dynamiska kolumnnamn
Din faktiska fråga är mer komplicerad, du vill också ha dynamiska kolumnnamn.
För en given tabell kan den resulterande frågan se ut så här:
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");
Svårigheten är att destillera dynamiska kolumnnamn. Antingen sätter du frågesträngen för hand eller (mycket hellre) låt den här funktionen göra det åt dig:
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$;
Ring:
SELECT f_generate_date10_sql(1);
Detta genererar den önskade frågan , som du kör i tur och ordning.
db<>fiol här