PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

PostgreSQL:ERROR:42601:cần có danh sách định nghĩa cột cho các hàm trả về bản ghi

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 QUERYUPDATE 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


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. CHỌN hoặc CHÈN một hàng trong một lệnh

  2. CHÈN SQL mà không chỉ định cột. Điều gì xảy ra?

  3. CONSTRAINT để kiểm tra các giá trị từ một bảng có liên quan từ xa (thông qua phép nối, v.v.)

  4. Hướng dẫn sử dụng pgBouncer cho PostgreSQL

  5. Làm cách nào để nhập tệp .sql vào cơ sở dữ liệu Heroku postgres của tôi?