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) 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$ LANGUAGE plpgsql;
Gọi:
SELECT * FROM word_frequency(123);
Giải trình:
Thực tế hơn nhiều là xác định rõ ràng kiểu trả về hơn là chỉ khai báo nó dưới dạng bản ghi. 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
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 cơ quan 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 tôi.
Nhưng lưu ý rằng xung đột đặt tên tiềm ẩn 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 cả 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 các mục trong danh sách SELECT:
ORDER BY 2 DESC
. Thí dụ:
- 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:
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à là tên hàm cơ bản và "text" là 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.
Đã thêm lỗi bị thiếu ;
và sửa lỗi cú pháp trong tiêu đề. (_max_tokens int)
, không phải (int maxTokens)
- nhập 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. Tốt hơn nữa: làm việc với numeric
(hoặc một loại dấu phẩy động). Xem bên dưới.
Thay thế
Đây là những gì tôi nghĩ rằng truy vấn của bạn sẽ thực sự trông như thế nào (tính toán một phần tương đối cho 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) 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$ LANGUAGE plpgsql;
Biểu thức sum(t.cnt) OVER ()
là một hàm 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.
Một câu lệnh rõ ràngRETURN
cuối cùng là không bắt buộc (nhưng được phép) khi làm việc với OUT
các tham số hoặc RETURNS TABLE
(sử dụng ngầm định các 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à sum()
hơn điều này bigint
tạo ra một numeric
kết quả, do đó chúng tôi xử lý một numeric
số tự động và mọi thứ chỉ rơi vào vị trí.