Đ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ủaSELECT * FROM tbl;
. -
Chi tiết về
format()
trong sách hướng dẫn.