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

Trả về bảng động với các cột không xác định từ hàm PL / pgSQL

Điều này khó giải quyết vì SQL yêu cầu biết kiểu trả về tại thời điểm gọi .
Ngoài ra, hàm plpgsql cần có kiểu trả về được xác định rõ .

Nếu bạn chọn trả lại hồ sơ ẩn danh , bạn nhận được những gì bạn đã xác định:hồ sơ ẩn danh. Postgres không biết những gì bên trong. Do đó, danh sách định nghĩa cột là bắt buộc để phân tách loại.

Có nhiều cách giải quyết khác nhau, tùy thuộc vào yêu cầu chính xác. Nếu bạn có bất kỳ cách nào để biết loại trả lại tại thời điểm cuộc gọi , Tôi đề xuất loại đa hình như được nêu trong chương cuối cùng của câu trả lời này ("Nhiều loại bảng hoàn chỉnh khác nhau"):
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

Nhưng điều đó không bao gồm việc thêm một cột khác vào kiểu trả về lúc chạy bên trong hàm . Điều đó chỉ là không thể. Tôi sẽ suy nghĩ lại toàn bộ cách tiếp cận .

Đối với cách tiếp cận hiện tại của bạn, thứ gần nhất mà tôi có thể nghĩ đến sẽ là một bảng tạm thời (hoặc một con trỏ) mà bạn truy vấn trong cuộc gọi thứ hai trong một giao dịch duy nhất .

Bạn gặp một số vấn đề khác trong mã của mình . Xem ghi chú bên dưới.

Bằng chứng về khái niệm

CREATE OR REPLACE FUNCTION f_tbl_plus_infowindow (_tbl regclass) -- regclass!
  RETURNS void AS  -- no direct return type
$func$
DECLARE
   -- appending _tmp for temp table
   _tmp text := quote_ident(_tbl::text || '_tmp');
BEGIN

-- Create temp table only for duration of transaction
EXECUTE format(
   'CREATE TEMP TABLE %s ON COMMIT DROP AS TABLE %s LIMIT 0', _tmp, _tbl);

IF EXISTS (
   SELECT 1
   FROM   pg_attribute a
   WHERE  a.attrelid = _tbl
   AND    a.attname  = 'infowindow'
   AND    a.attisdropped = FALSE)
THEN
   EXECUTE format('INSERT INTO %s SELECT * FROM %s', _tmp, _tbl);
ELSE
  -- This is assuming a NOT NULL column named "id"!
   EXECUTE format($x$
      ALTER  TABLE %1$s ADD COLUMN infowindow text;
      INSERT INTO %1$s
      SELECT *, 'ID: ' || id::text
      FROM   %2$s $x$
     ,_tmp, _tbl);
END IF;

END
$func$ LANGUAGE plpgsql;

Cuộc gọi phải được thực hiện trong một giao dịch duy nhất. Bạn có thể phải bắt đầu một giao dịch rõ ràng, tùy thuộc vào khách hàng của bạn.

BEGIN;
SELECT f_tbl_plus_infowindow ('tbl');
SELECT * FROM tbl_tmp;  -- do something with the returned rows
ROLLBACK;               -- or COMMIT, does not matter here

SQL Fiddle.

Ngoài ra, bạn có thể để bảng tạm thời tồn tại trong suốt thời gian của phiên. Tuy nhiên, hãy cảnh giác với những va chạm đặt tên với các cuộc gọi lặp lại.

Ghi chú

  • Sử dụng tên thông số thay vì ALIAS đã lỗi thời lệnh.

  • Để thực sự "mặc định" cho lược đồ hiện tại, hãy sử dụng truy vấn đơn giản hơn mà tôi hiển thị. Sử dụng regclass thực hiện thủ thuật tự động. Chi tiết:

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

    Ngoài ra, điều này cũng tránh lỗi cú pháp và Chèn SQL có thể xảy ra từ các tên bảng không chuẩn (hoặc có định dạng độc hại) trong mã gốc của bạn.

  • Mã trong ELSE của bạn mệnh đề hoàn toàn không hoạt động.

  • TABLE tbl; về cơ bản là viết tắt của SELECT * FROM tbl; .

  • Chi tiết về format() trong sách hướng dẫn.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. K-Nearest Neighbor Query trong PostGIS

  2. Django số lượng lớn_create với các hàng bỏ qua gây ra lỗi IntegrityError?

  3. 2 cách để có được kích thước của cơ sở dữ liệu trong PostgreSQL

  4. Làm thế nào để khôi phục tệp kết xuất PostgreSQL vào cơ sở dữ liệu Postgres?

  5. Cách thay đổi ngôn ngữ khi định dạng số trong PostgreSQL