Nie wygląda to podejrzanie, ale to piekielne pytanie .
Założenia
- Twoje liczby są
liczbami całkowitymi
. - Wszystkie kolumny w księdze tabel są zdefiniowane
NOT NULL
. -
Złożony
(nazwa, identyfikator, data)
jest unikalny w tabeliksiążka
. Powinieneś miećUNIKALNY
ograniczenie, najlepiej (dla wydajności) z kolumnami w tym zamówienie:UNIQUE(sid, date, name)
Zapewnia to automatycznie indeks potrzebny do wydajności. (W przeciwnym razie utwórz.) Zobacz:
crosstab()
zapytania
Aby uzyskać najwyższą wydajność i krótkie ciągi zapytań (zwłaszcza jeśli często uruchamiasz to zapytanie) sugeruję dodatkowy moduł tablefunc
udostępnianie różnych crosstab()
Funkcje. Podstawowe instrukcje:
Podstawowe zapytania
Najpierw musisz to zrobić dobrze.
Ostatnie 10 dni:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
Liczby z ostatnich 10 dni przy użyciu funkcji okna 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;
(Nie obejmuje rzeczywistych dat w tym zapytaniu).
Nazwy kolumn dla kolumn wyjściowych (dla pełnego rozwiązania):
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;
Prosty wynik ze statycznymi nazwami kolumn
To może być dla Ciebie wystarczające, ale w wyniku nie widzimy rzeczywistych dat:
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);
Do wielokrotnego użytku sugeruję utworzenie tej (bardzo szybkiej) ogólnej funkcji C dla 10 kolumn liczb całkowitych raz, aby trochę uprościć:
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';
Szczegóły w tej powiązanej odpowiedzi:
Wtedy Twoja rozmowa stanie się:
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!
Pełne rozwiązanie z dynamicznymi nazwami kolumn
Twoje rzeczywiste pytanie jest bardziej skomplikowane, potrzebujesz także dynamicznych nazw kolumn.
Dla danej tabeli wynikowe zapytanie może wyglądać tak:
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");
Trudność polega na wydestylowaniu dynamicznych nazw kolumn. Albo złóż łańcuch zapytania ręcznie, albo (raczej) pozwól tej funkcji zrobić to za Ciebie:
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$;
Zadzwoń:
SELECT f_generate_date10_sql(1);
To generuje żądane zapytanie , które wykonujesz po kolei.
db<>fiddle tutaj