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

Cập nhật nhiều cột trong một hàm kích hoạt trong plpgsql

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 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 trong EXECUTE . Tôi vượt qua NEW dưới dạng một tham số duy nhất với USING mệnh đề của EXECUTE .

  • 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ằng string_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 INSERTUPDATE .

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ặc format() 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ệt TG_TABLE_SCHEMATG_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ủy INSERT 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 cho INSERT 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ào INSERT , giống như với UPDATE .

  • 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ụng OLD ngoài ra.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Openshift và net-ssh không tương thích? (2.9.3-beta1 so với 2.9.2)

  2. Các cải tiến về báo cáo tiến độ trong PostgreSQL 12

  3. Cách hoạt động của Acosh () trong PostgreSQL

  4. Làm cách nào để sử dụng tên cột động trong câu lệnh UPDATE hoặc SELECT trong một hàm?

  5. Kiểm tra KHÔNG ĐẦY ĐỦ cho một bản ghi không trả về TRUE khi biến được đặt