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

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

SQL động và RETURN


Bạn muốn thực thi SQL động . Về cơ bản, điều đó thật đơn giản trong plpgsql với sự trợ giúp của EXECUTE . Bạn không cần một con trỏ. Trên thực tế, hầu hết thời gian bạn sẽ tốt hơn nếu không có con trỏ rõ ràng.

Sự cố bạn gặp phải:bạn muốn trả về các bản ghi thuộc loại chưa xác định . Một hàm cần khai báo kiểu trả về của nó trong RETURNS mệnh đề (hoặc với OUT hoặc INOUT thông số). Trong trường hợp của bạn, bạn sẽ phải quay lại hồ sơ ẩn danh, vì số , tên loại của các cột trả về khác nhau. Như:

CREATE FUNCTION data_of(integer)
  RETURNS SETOF record AS ...

Tuy nhiên, điều này không đặc biệt hữu ích. Bạn phải cung cấp danh sách định nghĩa cột với mọi cuộc gọi. Như:

SELECT * FROM data_of(17)
AS foo (colum_name1 integer
      , colum_name2 text
      , colum_name3 real);

Nhưng bạn sẽ làm điều này như thế nào, khi bạn không biết trước các cột?
Bạn có thể sử dụng các kiểu dữ liệu tài liệu có cấu trúc ít hơn như json , jsonb , hstore hoặc xml . Xem:

  • Cách lưu trữ bảng dữ liệu trong cơ sở dữ liệu?

Tuy nhiên, với mục đích của câu hỏi này, giả sử bạn muốn trả về các cột riêng lẻ, được nhập chính xác và được đặt tên nhiều nhất có thể.

Giải pháp đơn giản với kiểu trả về cố định

Cột datahora dường như là một cho trước, tôi sẽ giả sử loại dữ liệu timestamp và luôn có thêm hai cột với tên và kiểu dữ liệu khác nhau.

Tên chúng tôi sẽ bỏ qua các tên chung chung trong kiểu trả về.
Loại chúng tôi cũng sẽ từ bỏ và chuyển tất cả thành text kể từ mọi kiểu dữ liệu có thể được chuyển thành text .

CREATE OR REPLACE FUNCTION data_of(_id integer)
  RETURNS TABLE (datahora timestamp, col2 text, col3 text)
  LANGUAGE plpgsql AS
$func$
DECLARE
   _sensors text := 'col1::text, col2::text';  -- cast each col to text
   _type    text := 'foo';
BEGIN
   RETURN QUERY EXECUTE '
      SELECT datahora, ' || _sensors || '
      FROM   ' || quote_ident(_type) || '
      WHERE  id = $1
      ORDER  BY datahora'
   USING  _id;
END
$func$;

Các biến _sensors_type thay vào đó có thể là các tham số đầu vào.

Lưu ý RETURNS TABLE mệnh đề.

Lưu ý việc sử dụng RETURN QUERY EXECUTE . Đó là một trong những cách thanh lịch hơn để trả về các hàng từ một truy vấn động.

Tôi sử dụng tên cho tham số hàm, chỉ để tạo USING mệnh đề của RETURN QUERY EXECUTE ít khó hiểu hơn. $1 trong chuỗi SQL không tham chiếu đến tham số hàm mà là giá trị được truyền với USING mệnh đề. (Cả hai đều là $1 trong phạm vi tương ứng của chúng trong ví dụ đơn giản này.)

Lưu ý giá trị mẫu cho _sensors :mỗi cột được ép kiểu text .

Loại mã này rất dễ bị chèn SQL . Tôi sử dụng quote_ident() để bảo vệ chống lại nó. Kết hợp một số tên cột với nhau trong biến _sensors ngăn chặn việc sử dụng quote_ident() (và thường là một ý tưởng tồi!). Đảm bảo rằng không có nội dung xấu nào có thể có trong đó theo cách khác, chẳng hạn như bằng cách chạy riêng từng tên cột thông qua quote_ident() thay thế. VARIADIC tham số xuất hiện trong tâm trí ...

Đơn giản hơn kể từ PostgreSQL 9.1

Với phiên bản 9.1 trở lên, bạn có thể sử dụng format() để đơn giản hóa hơn nữa:

RETURN QUERY EXECUTE format('
   SELECT datahora, %s  -- identifier passed as unescaped string
   FROM   %I            -- assuming the name is provided by user
   WHERE  id = $1
   ORDER  BY datahora'
  ,_sensors, _type)
USING  _id;

Một lần nữa, các tên cột riêng lẻ có thể được thoát đúng cách và sẽ là một cách rõ ràng.

Số lượng biến có cùng loại cột

Sau khi câu hỏi của bạn cập nhật, có vẻ như loại trả lại của bạn đã

  • một số biến trong số các cột
  • nhưng tất cả các cột cùng một loại double precision (bí danh float8 )

Sử dụng ARRAY gõ trong trường hợp này để lồng một số giá trị thay đổi. Ngoài ra, tôi trả về một mảng có tên cột:

CREATE OR REPLACE FUNCTION data_of(_id integer)
  RETURNS TABLE (datahora timestamp, names text[], values float8[])
  LANGUAGE plpgsql AS
$func$
DECLARE
   _sensors text := 'col1, col2, col3';  -- plain list of column names
   _type    text := 'foo';
BEGIN
   RETURN QUERY EXECUTE format('
      SELECT datahora
           , string_to_array($1)  -- AS names
           , ARRAY[%s]            -- AS values
      FROM   %s
      WHERE  id = $2
      ORDER  BY datahora'
    , _sensors, _type)
   USING  _sensors, _id;
END
$func$;

Nhiều loại bảng hoàn chỉnh khác nhau

Để thực sự trả về tất cả các cột của bảng , có một giải pháp đơn giản, mạnh mẽ bằng cách sử dụng loại đa hình :

CREATE OR REPLACE FUNCTION data_of(_tbl_type anyelement, _id int)
  RETURNS SETOF anyelement
  LANGUAGE plpgsql AS
$func$
BEGIN
   RETURN QUERY EXECUTE format('
      SELECT *
      FROM   %s  -- pg_typeof returns regtype, quoted automatically
      WHERE  id = $1
      ORDER  BY datahora'
    , pg_typeof(_tbl_type))
   USING  _id;
END
$func$;

Gọi (quan trọng!):

SELECT * FROM data_of(NULL::pcdmet, 17);

Thay thế pcdmet trong cuộc gọi với bất kỳ tên bảng nào khác.

Điều này hoạt động như thế nào?

anyelement là một kiểu dữ liệu giả, một kiểu đa hình, một trình giữ chỗ cho bất kỳ kiểu dữ liệu không phải mảng nào. Tất cả các lần xuất hiện của anyelement trong chức năng đánh giá cùng loại được cung cấp tại thời điểm chạy. Bằng cách cung cấp giá trị của một kiểu đã xác định làm đối số cho hàm, chúng tôi xác định một cách ngầm định kiểu trả về.

PostgreSQL tự động xác định kiểu hàng (kiểu dữ liệu tổng hợp) cho mọi bảng được tạo, do đó, có một kiểu được xác định rõ ràng cho mọi bảng. Điều này bao gồm các bảng tạm thời, thuận tiện cho việc sử dụng đột xuất.

Bất kỳ loại nào cũng có thể là NULL . Bắt tay vào một NULL giá trị, truyền đến loại bảng: NULL::pcdmet .

Bây giờ hàm trả về một loại hàng được xác định rõ ràng và chúng ta có thể sử dụng SELECT * FROM data_of() để phân tách hàng và lấy các cột riêng lẻ.

pg_typeof(_tbl_type) trả về tên của bảng dưới dạng loại định danh đối tượng regtype . Khi được tự động chuyển đổi thành text , giá trị nhận dạng tự động được trích dẫn kép và đủ điều kiện về giản đồ nếu cần, tự động bảo vệ chống lại SQL injection. Điều này thậm chí có thể giải quyết các tên bảng đủ điều kiện giản đồ trong đó quote_ident() Sẽ thất bại. Xem:

  • Tên bảng dưới dạng tham số hàm PostgreSQL


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL - Viết sql động trong thủ tục được lưu trữ trả về tập kết quả

  2. Gặp lỗi khi ánh xạ cột PostgreSQL LTREE ở chế độ ngủ đông

  3. Cách triển khai một LMS canvas khả dụng cao với Cụm cơ sở dữ liệu PostgreSQL

  4. IntegrityError giá trị khóa trùng lặp vi phạm ràng buộc duy nhất - django / postgres

  5. Đặt lại bộ đếm gia tăng tự động trong postgres