Mặc dù câu trả lời của @ Gary là đúng về mặt kỹ thuật, nhưng anh ấy không đề cập đến rằng PostgreSQL có hỗ trợ biểu mẫu này:
UPDATE tbl
SET (col1, col2, ...) = (expression1, expression2, ..)
Đọc hướng dẫn về UPDATE
một lần nữa.
Vẫn còn khó khăn để hoàn thành công việc với SQL động. Vì bạn không chỉ định, tôi giả sử một trường hợp đơn giản trong đó các chế độ xem bao gồm các cột giống như các bảng bên dưới của chúng.
CREATE VIEW tbl_view AS SELECT * FROM tbl;
Sự cố
-
Bản ghi đặc biệt
NEW
không hiển thị bên trongEXECUTE
. Tôi vượt quaNEW
dưới dạng một tham số duy nhất vớiUSING
mệnh đề củaEXECUTE
. -
Như đã thảo luận,
UPDATE
với dạng danh sách cần các giá trị riêng lẻ . Tôi sử dụng một lựa chọn phụ để chia bản ghi thành các cột riêng lẻ:UPDATE ... FROM (SELECT ($1).*) x
(Dấu ngoặc quanh
$1
không phải là tùy chọn.) Điều này cho phép tôi chỉ cần sử dụng hai danh sách cột được tạo bằngstring_agg()
từ bảng danh mục:một có và một không có bảng. -
Không thể chỉ định tổng thể một giá trị hàng cho các cột riêng lẻ. Hướng dẫn sử dụng:
Theo tiêu chuẩn, giá trị nguồn cho danh sách phụ được đặt trong ngoặc đơn của các tên cột mục tiêu có thể là bất kỳ biểu thức có giá trị hàng nào với số lượng cột chính xác. PostgreSQL chỉ cho phép giá trị nguồn là một phương thức khởi tạo hàng hoặc một con
SELECT
. -
INSERT
được thực hiện đơn giản hơn. Giả sử cấu trúc của khung nhìn và bảng giống hệt nhau, tôi bỏ qua danh sách định nghĩa cột. (Có thể cải thiện, xem bên dưới.)
Giải pháp
Tôi đã thực hiện một số cập nhật cho cách tiếp cận của bạn để làm cho nó trở nên tỏa sáng.
Hàm kích hoạt cho UPDATE
:
CREATE OR REPLACE FUNCTION f_trg_up()
RETURNS TRIGGER AS
$func$
DECLARE
tbl text := quote_ident(TG_TABLE_SCHEMA) || '.'
|| quote_ident(substring(TG_TABLE_NAME from '(.+)_view$'));
cols text;
vals text;
BEGIN
SELECT INTO cols, vals
string_agg(quote_ident(attname), ', ')
,string_agg('x.' || quote_ident(attname), ', ')
FROM pg_attribute
WHERE attrelid = tbl::regclass
AND NOT attisdropped -- no dropped (dead) columns
AND attnum > 0; -- no system columns
EXECUTE format('
UPDATE %s t
SET (%s) = (%s)
FROM (SELECT ($1).*) x
WHERE t.id = ($2).id'
, tbl, cols, vals) -- assuming unique "id" in every table
USING NEW, OLD;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Hàm kích hoạt cho INSERT
:
CREATE OR REPLACE FUNCTION f_trg_ins()
RETURNS TRIGGER AS
$func$
DECLARE
tbl text := quote_ident(TG_TABLE_SCHEMA) || '.'
|| quote_ident(substring(TG_TABLE_NAME from '(.+)_view$'));
BEGIN
EXECUTE 'INSERT INTO ' || tbl || ' SELECT ($1).*'
USING NEW;
RETURN NEW; -- don't return NULL unless you know what you're doing
END
$func$ LANGUAGE plpgsql;
Kích hoạt:
CREATE TRIGGER trg_instead_up
INSTEAD OF UPDATE ON a_view
FOR EACH ROW EXECUTE PROCEDURE f_trg_up();
CREATE TRIGGER trg_instead_ins
INSTEAD OF INSERT ON a_view
FOR EACH ROW EXECUTE PROCEDURE f_trg_ins();
SQL Fiddle chứng minh INSERT
và UPDATE
.
Những điểm chính
-
Bao gồm tên lược đồ để làm rõ ràng tham chiếu bảng. Có thể có nhiều trường hợp của cùng một tên bảng trong cùng một cơ sở dữ liệu trong nhiều lược đồ!
-
Truy vấn
pg_attribute
thay vìinformation_schema.columns
. Đó là ít di động hơn, nhưng nhiều nhanh hơn và cho phép tôi sử dụng table-OID.- Cách kiểm tra xem một bảng có tồn tại trong một lược đồ nhất định không
-
Tên bảng KHÔNG an toàn trước SQLi khi được xử lý dưới dạng chuỗi giống như trong việc xây dựng truy vấn cho SQL động. Thoát với
quote_ident()
hoặcformat()
hoặc với kiểu nhận dạng đối tượng. Điều này bao gồm các biến hàm kích hoạt đặc biệtTG_TABLE_SCHEMA
vàTG_TABLE_NAME
! -
Truyền tới loại mã định danh đối tượng
regclass
để khẳng định tên bảng là hợp lệ và lấy OID để tra cứu danh mục. -
Tùy chọn sử dụng
format()
để xây dựng chuỗi truy vấn động một cách an toàn. -
Không cần SQL động cho truy vấn đầu tiên trên các bảng danh mục. Nhanh hơn, đơn giản hơn.
-
Sử dụng
RETURN NEW
thay vìRETURN NULL
trong các chức năng kích hoạt này trừ khi bạn biết mình đang làm gì. (NULL
sẽ hủyINSERT
cho hàng hiện tại.) -
Phiên bản đơn giản này giả định rằng mọi bảng (và chế độ xem) có một cột duy nhất có tên là
id
. Phiên bản phức tạp hơn có thể sử dụng động khóa chính. -
Hàm cho
UPDATE
cho phép các cột của chế độ xem và bảng ở bất kỳ thứ tự nào , miễn là bộ giống nhau. Hàm choINSERT
mong đợi các cột chế độ xem và bảng có thứ tự giống hệt nhau . Nếu bạn muốn cho phép thứ tự tùy ý, hãy thêm danh sách định nghĩa cột vàoINSERT
, giống như vớiUPDATE
. -
Phiên bản cập nhật cũng bao gồm các thay đổi đối với
id
bằng cách sử dụngOLD
ngoài ra.