Trả lại các cột đã chọn
CREATE OR REPLACE FUNCTION get_user_by_username(_username text
, _online bool DEFAULT false)
RETURNS TABLE (
user_id int
, user_name varchar
, last_activity timestamptz
)
LANGUAGE plpgsql AS
$func$
BEGIN
IF _online THEN
RETURN QUERY
UPDATE users u
SET last_activity = current_timestamp -- ts with time zone
WHERE u.user_name = _username
RETURNING u.user_id
, u.user_name
, u.last_activity;
ELSE
RETURN QUERY
SELECT u.user_id
, u.user_name
, u.last_activity
FROM users u
WHERE u.user_name = _username;
END IF;
END
$func$;
Gọi:
SELECT * FROM get_user_by_username('myuser', true);
Bạn có DECLARE result record;
nhưng không sử dụng biến. Tôi đã xóa điểm mấu chốt.
Bạn có thể trả lại hồ sơ trực tiếp từ UPDATE
, nhanh hơn nhiều so với việc gọi thêm một SELECT
tuyên bố. Sử dụng RETURN QUERY
và UPDATE
với RETURNING
mệnh đề.
Nếu người dùng không phải là _online
, mặc định thành SELECT
đơn giản . Đây cũng là mặc định (an toàn) nếu tham số thứ hai bị bỏ qua - chỉ có thể thực hiện được sau khi cung cấp mặc định đó với DEFAULT false
trong định nghĩa hàm.
Nếu bạn không có tên cột đủ điều kiện cho bảng (tablename.columnname
) trong các truy vấn bên trong hàm, hãy cảnh giác với xung đột đặt tên giữa tên cột và các tham số được đặt tên, hiển thị (hầu hết) ở mọi nơi bên trong một hàm.
Bạn cũng có thể tránh xung đột như vậy bằng cách sử dụng tham chiếu vị trí ($n
) cho các tham số. Hoặc sử dụng tiền tố mà bạn không bao giờ sử dụng cho tên cột:như dấu gạch dưới (_username
).
Nếu users.username
được xác định duy nhất trong bảng của bạn, rồi đến LIMIT 1
trong câu truy vấn thứ hai chỉ là cruft. Nếu nó không , sau đó là UPDATE
có thể cập nhật nhiều hàng, rất có thể sai . Tôi giả sử một username
duy nhất và giảm nhiễu.
Xác định kiểu trả về của hàm (như @ertx đã chứng minh) hoặc bạn phải cung cấp danh sách định nghĩa cột với mọi lệnh gọi hàm, điều này thật khó xử.
Tạo một kiểu cho mục đích đó (như @ertx đề xuất) là một cách tiếp cận hợp lệ, nhưng có thể là quá mức cần thiết cho một chức năng. Đó là cách thực hiện trong các phiên bản cũ của Postgres trước khi chúng tôi có RETURNS TABLE
cho mục đích đó - như đã trình bày ở trên.
Bạn không cần vòng lặp cho chức năng đơn giản này.
Mọi hàm đều cần khai báo ngôn ngữ. LANGUAGE plpgsql
trong trường hợp này.
Tôi sử dụng timestamptz
(timestamp with time zone
) thay vì timestamp
(timestamp without time zone
), là mặc định lành mạnh. Xem:
- Bỏ qua hoàn toàn các múi giờ trong Rails và PostgreSQL
Trả lại (tập hợp) toàn bộ hàng
Để trả về tất cả các cột trong bảng hiện có users
, có một cách đơn giản hơn. Postgres tự động xác định loại kết hợp cùng tên cho mọi bảng . Chỉ cần sử dụng người dùng RETURNS SETOF users
để đơn giản hóa đáng kể truy vấn:
CREATE OR REPLACE FUNCTION get_user_by_username(_username text
, _online bool DEFAULT false)
RETURNS SETOF users
LANGUAGE plpgsql AS
$func$
BEGIN
IF _online THEN
RETURN QUERY
UPDATE users u
SET last_activity = current_timestamp
WHERE u.user_name = _username
RETURNING u.*;
ELSE
RETURN QUERY
SELECT *
FROM users u
WHERE u.user_name = _username;
END IF;
END
$func$;
Trả lại toàn bộ hàng cộng với bổ sung tùy chỉnh
Để giải quyết câu hỏi được thêm bởi TheRealChx101 trong một bình luận bên dưới:
Điều gì sẽ xảy ra nếu bạn cũng có một giá trị được tính toán ngoài toàn bộ bảng? 😑
Không đơn giản, nhưng có thể làm được. Chúng tôi có thể gửi toàn bộ loại hàng dưới dạng một và thêm nhiều trường khác:
CREATE OR REPLACE FUNCTION get_user_by_username3(_username text
, _online bool DEFAULT false)
RETURNS TABLE (
users_row users
, custom_addition text
)
LANGUAGE plpgsql AS
$func$
BEGIN
IF _online THEN
RETURN QUERY
UPDATE users u
SET last_activity = current_timestamp -- ts with time zone
WHERE u.user_name = _username
RETURNING u -- whole row
, u.user_name || u.user_id;
ELSE
RETURN QUERY
SELECT u, u.user_name || u.user_id
FROM users u
WHERE u.user_name = _username;
END IF;
END
$func$;
"Phép thuật" nằm trong lệnh gọi hàm, nơi chúng tôi (tùy chọn) phân tách loại hàng:
SELECT (users_row).*, custom_addition FROM get_user_by_username('foo', true);
db <> fiddle here (hiển thị tất cả)
Nếu bạn cần thứ gì đó "năng động" hơn, hãy xem xét:
- Cấu trúc lại một hàm PL / pgSQL để trả về kết quả đầu ra của các truy vấn SELECT khác nhau