Điều này trông có vẻ khó tin, nhưng đó là một câu hỏi quái gở .
Giả định
- Số lượng của bạn là
số nguyên
. - Tất cả các cột trong sổ bảng được xác định
NOT NULL
. -
tổng hợp (tên, sid, ngày)
là duy nhất trong bảngbook
. Bạn phải cóUNIQUE
ràng buộc, tốt nhất là (đối với hiệu suất) với các cột trong this đặt hàng:UNIQUE(sid, date, name)
Điều này cung cấp chỉ số cần thiết cho hiệu suất một cách tự động. (Nếu khác, hãy tạo một cái khác.) Xem:
crosstab ()
truy vấn
Để có được hiệu suất cao nhất và chuỗi truy vấn ngắn (đặc biệt nếu bạn chạy truy vấn này thường xuyên), tôi đề xuất mô-đun bổ sung tablefunc
cung cấp các crosstab ()
khác nhau chức năng. Hướng dẫn cơ bản:
Truy vấn cơ bản
Trước tiên, bạn cần phải làm đúng những điều này.
10 ngày qua:
SELECT DISTINCT date
FROM book
WHERE sid = 1
ORDER BY date DESC
LIMIT 10;
Các con số trong 10 ngày qua sử dụng hàm window secure_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;
(Không bao gồm ngày thực tế trong truy vấn này.)
Tên cột cho các cột đầu ra (cho giải pháp đầy đủ):
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;
Kết quả đơn giản với tên cột tĩnh
Điều này có thể đủ tốt cho bạn - nhưng chúng tôi không thấy ngày thực tế trong kết quả:
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);
Để sử dụng nhiều lần, tôi khuyên bạn nên tạo hàm C chung (rất nhanh) này cho 10 cột số nguyên một lần, để đơn giản hóa mọi thứ một chút:
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';
Chi tiết trong câu trả lời liên quan này:
Sau đó, cuộc gọi của bạn trở thành:
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!
Giải pháp đầy đủ với tên cột động
Câu hỏi thực tế của bạn phức tạp hơn, bạn cũng muốn có các tên cột động.
Đối với một bảng nhất định, khi đó truy vấn kết quả có thể trông như thế này:
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");
Khó khăn là chắt lọc các tên cột động. Hãy tập hợp chuỗi truy vấn bằng tay hoặc (nhiều hơn là) để hàm này làm việc đó cho bạn:
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$;
Gọi:
SELECT f_generate_date10_sql(1);
Điều này tạo truy vấn mong muốn , mà bạn thực hiện lần lượt.
db <> fiddle tại đây