Làm việc với bảng giả này
CREATE TEMP TABLE foo (id int, my_num numeric);
INSERT INTO foo VALUES (1, 12.34)
Đầu tiên, tôi đã đơn giản hóa và làm sạch ví dụ của bạn:
-
Đã xóa một số nhiễu không liên quan đến câu hỏi.
-
RETURNS SETOF void
hầu như không có ý nghĩa. Tôi sử dụngRETURNS void
thay vào đó. -
Tôi sử dụng
text
thay vìcharacter varying
, chỉ vì mục đích đơn giản. -
Khi sử dụng SQL động, bạn có để bảo vệ khỏi việc đưa vào SQL, tôi sử dụng
format()
với%I
trong trường hợp này. Có nhiều cách khác.
Vấn đề cơ bản là SQL rất cứng nhắc với các kiểu và định danh. Bạn đang thao tác với bảng động tên cũng như với tên trường động của bản ghi - một ẩn danh ghi lại trong ví dụ ban đầu của bạn. Pl / pgSQL không được trang bị tốt để đối phó với điều này. Postgres không biết có gì bên trong một bản ghi ẩn danh. Chỉ sau khi bạn gán bản ghi cho loại nổi tiếng bạn có thể tham khảo các trường riêng lẻ không.
Đây là một câu hỏi có liên quan chặt chẽ, cố gắng đặt một trường của bản ghi có tên động:
Cách đặt giá trị của trường biến tổng hợp bằng cách sử dụng SQL động
Chức năng cơ bản
CREATE OR REPLACE FUNCTION getrowdata1(table_name text, id int)
RETURNS void AS
$func$
DECLARE
srowdata record;
reqfield text := 'my_num'; -- assigning at declaration time for convenience
value numeric;
BEGIN
RAISE NOTICE 'id: %', id;
EXECUTE format('SELECT * FROM %I WHERE id = $1', table_name)
USING id
INTO srowdata;
RAISE NOTICE 'srowdata: %', srowdata;
RAISE NOTICE 'srowdatadata.my_num: %', srowdata.my_num;
/* This does not work, even with dynamic SQL
EXECUTE format('SELECT ($1).%I', reqfield)
USING srowdata
INTO value;
RAISE NOTICE 'value: %', value;
*/
END
$func$ LANGUAGE plpgsql;
Gọi:
SELECT * from getrowdata1('foo', 1);
Phần được nhận xét sẽ nêu ra một ngoại lệ:
không thể xác định cột "my_num" trong loại dữ liệu bản ghi:SELECT * fromgetrowdata (1, 'foo')
hstore
Bạn cần cài đặt mô-đun bổ sung hstore cho điều này. Mỗi cơ sở dữ liệu một lần với:
CREATE EXTENSION hstore;
Sau đó, tất cả có thể hoạt động như thế này:
CREATE OR REPLACE FUNCTION getrowdata2(table_name text, id int)
RETURNS void AS
$func$
DECLARE
hstoredata hstore;
reqfield text := 'my_num';
value numeric;
BEGIN
RAISE NOTICE 'id: %', id;
EXECUTE format('SELECT hstore(t) FROM %I t WHERE id = $1', table_name)
USING id
INTO hstoredata;
RAISE NOTICE 'hstoredata: %', hstoredata;
RAISE NOTICE 'hstoredata.my_num: %', hstoredata -> 'my_num';
value := hstoredata -> reqfield;
RAISE NOTICE 'value: %', value;
END
$func$ LANGUAGE plpgsql;
Gọi:
SELECT * from getrowdata2('foo', 1);
Kiểu đa hình
Thay thế mà không cần cài đặt các mô-đun bổ sung.
Vì bạn chọn toàn bộ hàng vào biến bản ghi của mình, nên có một loại được xác định rõ ràng cho nó theo định nghĩa. Sử dụng nó. Từ khóa là các loại đa hình .
CREATE OR REPLACE FUNCTION getrowdata3(_tbl anyelement, id int)
RETURNS void AS
$func$
DECLARE
reqfield text := 'my_num';
value numeric;
BEGIN
RAISE NOTICE 'id: %', id;
EXECUTE format('SELECT * FROM %s WHERE id = $1', pg_typeof(_tbl))
USING id
INTO _tbl;
RAISE NOTICE '_tbl: %', _tbl;
RAISE NOTICE '_tbl.my_num: %', _tbl.my_num;
EXECUTE 'SELECT ($1).' || reqfield -- requfield must be SQLi-safe or escape
USING _tbl
INTO value;
RAISE NOTICE 'value: %', value;
END
$func$ LANGUAGE plpgsql;
Gọi:
SELECT * from getrowdata3(NULL::foo, 1);
-> SQLfiddle
-
Tôi (ab-) sử dụng tham số đầu vào
_tbl
cho ba mục đích ở đây:- Cung cấp loại được xác định rõ ràng của hồ sơ
- Cung cấp tên của bảng, tự động đủ điều kiện giản đồ
- Phục vụ như một sự thay đổi.
-
Giải thích thêm trong câu trả lời có liên quan này (chương cuối):
Refactor một hàm PL / pgSQL để trả về kết quả đầu ra của các truy vấn SELECT khác nhau