Това не изглежда подозрително, но е дяволски въпрос .
Предположения
- Броят ви е
цяло число
. - Всички колони в книгата с таблици са дефинирани
NOT NULL
. -
Съставният
(име, sid, дата)
е уникален в таблицаbook
. Трябва да иматеУНИКАЛЕН
ограничение, за предпочитане (за ефективност) с колони в това ред:UNIQUE(sid, date, name)
Това автоматично осигурява индекса, необходим за производителност. (В противен случай създайте такъв.) Вижте:
crosstab()
заявки
За да получите максимална производителност и кратки низове на заявки (особено ако изпълнявате тази заявка често), предлагам допълнителния модул tablefunc
осигуряване на различни crosstab()
функции. Основни инструкции:
Основни заявки
Първо трябва да ги направите правилно.
Последните 10 дни:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
Числа за последните 10 дни с помощта на прозоречната функция 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;
(Без включването на действителни дати в тази заявка.)
Имена на колони за изходни колони (за пълно решение):
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;
Прост резултат със статични имена на колони
Това може да е достатъчно за вас, но не виждаме действителни дати в резултата:
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);
За многократна употреба ви предлагам да създадете тази (много бърза) обща C функция за 10 цели колони веднъж, за да опростите малко нещата:
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';
Подробности в този свързан отговор:
Тогава вашето обаждане става:
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!
Пълно решение с динамични имена на колони
Действителният ви въпрос е по-сложен, вие също искате динамични имена на колони.
За дадена таблица получената заявка може да изглежда така:
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");
Трудността е да се дестилират имена на динамични колони. Или сглобете низа на заявката на ръка, или (по-скоро) оставете тази функция да го направи вместо вас:
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$;
Обаждане:
SELECT f_generate_date10_sql(1);
Това генерира желаната заявка , които изпълнявате на свой ред.
db<>fiddle тук