Truy vấn tốt hơn
Đối với người mới bắt đầu, bạn có thể sửa cú pháp, đơn giản hóa và làm rõ một chút:
SELECT *
FROM (
SELECT p.person_id, p.name, p.team, sum(s.score)::int AS score
,rank() OVER (PARTITION BY p.team
ORDER BY sum(s.score) DESC)::int AS rnk
FROM person p
JOIN score s USING (person_id)
GROUP BY 1
) sub
WHERE rnk < 3;
-
Xây dựng trên bố cục bảng cập nhật của tôi. Xem fiddle bên dưới.
-
Bạn không cần truy vấn phụ bổ sung. Các chức năng của cửa sổ được thực thi sau tổng hợp các chức năng, vì vậy bạn có thể lồng ghép nó như đã trình bày.
-
Trong khi nói về "xếp hạng", bạn có thể muốn sử dụng
rank()
, không phảirow_number()
. -
Giả sử
people.people_id
là PK, bạn có thể đơn giản hóaGROUP BY
. -
Đảm bảo xác định bảng đủ điều kiện cho tất cả các tên cột có thể không rõ ràng
Hàm PL / pgSQL
Sau đó, tôi sẽ viết một hàm plpgsql nhận tham số cho các phần biến của bạn. Thực hiện a
- c
điểm của bạn. d
không rõ ràng, để lại cho bạn thêm.
CREATE OR REPLACE FUNCTION f_demo(_agg text DEFAULT 'sum'
, _left_join bool DEFAULT FALSE
, _where_name text DEFAULT NULL)
RETURNS TABLE(person_id int, name text, team text, score int, rnk int) AS
$func$
DECLARE
_agg_op CONSTANT text[] := '{count, sum, avg}'; -- allowed functions
_sql text;
BEGIN
-- assert --
IF _agg ILIKE ANY (_agg_op) THEN
-- all good
ELSE
RAISE EXCEPTION '_agg must be one of %', _agg_op;
END IF;
-- query --
_sql := format('
SELECT *
FROM (
SELECT p.person_id, p.name, p.team, %1$s(s.score)::int AS score
,rank() OVER (PARTITION BY p.team
ORDER BY %1$s(s.score) DESC)::int AS rnk
FROM person p
%2$s score s USING (person_id)
%3$s
GROUP BY 1
) sub
WHERE rnk < 3
ORDER BY team, rnk'
, _agg
, CASE WHEN _left_join THEN 'LEFT JOIN' ELSE 'JOIN' END
, CASE WHEN _where_name <> '' THEN 'WHERE p.name LIKE $1' ELSE '' END
);
-- debug -- quote when tested ok
-- RAISE NOTICE '%', _sql;
-- execute -- unquote when tested ok
RETURN QUERY EXECUTE _sql
USING _where_name; -- $1
END
$func$ LANGUAGE plpgsql;
Gọi:
SELECT * FROM f_demo();
SELECT * FROM f_demo('sum', TRUE, '%2');
SELECT * FROM f_demo('avg', FALSE);
SELECT * FROM f_demo(_where_name := '%1_'); -- named param
-
Bạn cần hiểu rõ về PL / pgSQL. Khác, có quá nhiều thứ để giải thích. Bạn sẽ tìm thấy các câu trả lời liên quan tại đây trên SO trong plpgsql thực tế mọi chi tiết trong câu trả lời.
-
Tất cả các tham số được xử lý một cách an toàn, không thể chèn SQL. Thêm:
-
Đặc biệt lưu ý, cách một
WHERE
mệnh đề được thêm có điều kiện (khi_where_name
được truyền) với tham số vị trí$1
trong câu truy vấn. Giá trị được chuyển đếnEXECUTE
như giá trị vớiUSING
mệnh đề . Không có chuyển đổi kiểu, không có lối thoát, không có cơ hội để đưa vào SQL. Ví dụ: -
Sử dụng
DEFAULT
giá trị cho các tham số hàm, vì vậy bạn có thể tự do cung cấp bất kỳ hoặc không. Thêm: -
Hàm Định dạng
format()
là công cụ để xây dựng các chuỗi SQL động phức tạp theo cách an toàn và sạch sẽ.