Sử dụng RETURN QUERY
:
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text -- also visible as OUT parameter inside function
, cnt bigint
, ratio bigint)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT t.txt
, count(*) AS cnt -- column alias only visible inside
, (count(*) * 100) / _max_tokens -- I added brackets
FROM (
SELECT t.txt
FROM token t
WHERE t.chartype = 'ALPHABETIC'
LIMIT _max_tokens
) t
GROUP BY t.txt
ORDER BY cnt DESC; -- potential ambiguity
END
$func$;
Gọi:
SELECT * FROM word_frequency(123);
Xác định kiểu trả về một cách rõ ràng là nhiều thiết thực hơn là trả về một record
chung chung . Bằng cách này, bạn không phải cung cấp danh sách định nghĩa cột với mọi lệnh gọi hàm. RETURNS TABLE
là một cách để làm điều đó. Co nhung nguoi khac. Các kiểu dữ liệu của OUT
các tham số phải khớp chính xác với những gì được trả về bởi truy vấn.
Chọn tên cho OUT
các thông số một cách cẩn thận. Chúng có thể nhìn thấy trong phần thân chức năng ở hầu hết mọi nơi. Bảng phân loại các cột có cùng tên để tránh xung đột hoặc kết quả không mong muốn. Tôi đã làm điều đó cho tất cả các cột trong ví dụ của mình.
Nhưng lưu ý rằng xung đột đặt tên có thể xảy ra giữa OUT
tham số cnt
và bí danh cột cùng tên. Trong trường hợp cụ thể này (RETURN QUERY SELECT ...
) Postgres sử dụng bí danh cột trên OUT
tham số theo một trong hai cách. Tuy nhiên, điều này có thể không rõ ràng trong các ngữ cảnh khác. Có nhiều cách khác nhau để tránh bất kỳ sự nhầm lẫn nào:
- Sử dụng vị trí thứ tự của mục trong danh sách CHỌN:
ORDER BY 2 DESC
. Ví dụ:- Chọn hàng đầu tiên trong mỗi GROUP BY nhóm?
- Lặp lại biểu thức
ORDER BY count(*)
. - (Không áp dụng ở đây.) Đặt tham số cấu hình
plpgsql.variable_conflict
hoặc sử dụng lệnh đặc biệt#variable_conflict error | use_variable | use_column
trong hàm. Xem:- Xung đột đặt tên giữa tham số hàm và kết quả của mệnh đề JOIN với USING
Không sử dụng "văn bản" hoặc "đếm" làm tên cột. Cả hai đều hợp pháp để sử dụng trong Postgres, nhưng "count" là một từ dành riêng trong SQL tiêu chuẩn và một tên hàm cơ bản và "văn bản" là một kiểu dữ liệu cơ bản. Có thể dẫn đến những lỗi khó hiểu. Tôi sử dụng txt
và cnt
trong các ví dụ của tôi, bạn có thể muốn các tên rõ ràng hơn.
Đã thêm một ;
bị thiếu và sửa lỗi cú pháp trong tiêu đề. (_max_tokens int)
, không phải (int maxTokens)
- loại sau tên .
Trong khi làm việc với phép chia số nguyên, tốt hơn nên nhân trước và chia sau, để giảm thiểu lỗi làm tròn. Hoặc làm việc với numeric
hoặc một loại dấu chấm động. Xem bên dưới.
Thay thế
Đây là những gì tôi nghĩ truy vấn của bạn thực sự sẽ trông như thế nào (tính toán chia sẻ tương đối trên mỗi mã thông báo ):
CREATE OR REPLACE FUNCTION word_frequency(_max_tokens int)
RETURNS TABLE (txt text
, abs_cnt bigint
, relative_share numeric)
LANGUAGE plpgsql AS
$func$
BEGIN
RETURN QUERY
SELECT t.txt, t.cnt
, round((t.cnt * 100) / (sum(t.cnt) OVER ()), 2) -- AS relative_share
FROM (
SELECT t.txt, count(*) AS cnt
FROM token t
WHERE t.chartype = 'ALPHABETIC'
GROUP BY t.txt
ORDER BY cnt DESC
LIMIT _max_tokens
) t
ORDER BY t.cnt DESC;
END
$func$;
Biểu thức sum(t.cnt) OVER ()
là một chức năng cửa sổ. Bạn có thể sử dụng CTE thay vì truy vấn con. Khá, nhưng truy vấn con thường rẻ hơn trong những trường hợp đơn giản như thế này (hầu hết trước Postgres 12).
RETURN
rõ ràng cuối cùng tuyên bố là không bắt buộc (nhưng được phép) khi làm việc với OUT
tham số hoặc RETURNS TABLE
(sử dụng ẩn OUT
tham số).
round()
với hai tham số chỉ hoạt động cho numeric
các loại. count()
trong truy vấn con tạo ra một bigint
kết quả và một sum()
qua bigint
này tạo ra một numeric
kết quả, do đó chúng tôi xử lý một numeric
đánh số tự động và mọi thứ sẽ vào đúng vị trí.