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

Hàm postgres trả về một hàng dưới dạng giá trị JSON

Tôi thấy hai vấn đề chính:
1. Bạn không thể đặt UPDATE vào một truy vấn con hoàn toàn . Bạn có thể giải quyết điều đó với sửa đổi dữ liệu CTE như Patrick thể hiện , nhưng điều đó đắt hơn và dài dòng hơn mức cần thiết đối với trường hợp hiện tại.
2. Bạn có xung đột đặt tên tiềm ẩn nguy hiểm , điều đó vẫn chưa được giải quyết.

Truy vấn / chức năng tốt hơn

Tạm thời để trình bao bọc hàm SQL sang một bên (chúng ta sẽ quay lại vấn đề đó). Bạn có thể sử dụng UPDATE đơn giản với RETURNING mệnh đề:

UPDATE tbl
SET    value1 = 'something_new'
WHERE  id = 123
RETURNING row_to_json(ROW(value1, value2));

RETURNING mệnh đề cho phép các biểu thức tùy ý liên quan đến các cột của hàng được cập nhật. Điều đó ngắn hơn và rẻ hơn CTE sửa đổi dữ liệu.

Vấn đề còn lại:hàm tạo hàng ROW(...) không bảo toàn tên cột (đây là một điểm yếu đã biết), vì vậy bạn nhận được các khóa chung trong giá trị JSON của mình:

row_to_json
{"f1":"something_new","f2":"what ever is in value2"}

Trong Postgres 9.3, bạn sẽ cần một CTE chức năng khác để đóng gói bước đầu tiên hoặc truyền thành một loại hàng được xác định rõ ràng. Chi tiết:

Trong Postgres 9.4 chỉ cần sử dụng json_build_object() hoặc json_object() :

UPDATE tbl
SET    value1 = 'something_new'
WHERE  id = 123
RETURNING json_build_object('value1', value1, 'value2', value2);

Hoặc:

...
RETURNING json_object('{value1, value2}', ARRAY[value1, value2]);

Bây giờ bạn nhận được tên cột ban đầu hoặc bất kỳ tên nào bạn đã chọn làm tên khóa:

row_to_json
{"value1":"something_new","value2":"what ever is in value2"}

Thật dễ dàng để tóm gọn điều này trong một hàm, điều này đưa chúng tôi đến vấn đề thứ hai của bạn ...

Xung đột đặt tên

Trong hàm ban đầu, bạn sử dụng các tên giống hệt nhau cho các tham số hàm và tên cột. Đây thường là một ý tưởng rất tồi . Bạn sẽ cần phải hiểu một cách sâu sắc số nhận dạng nào đứng trước trong phạm vi nào.

Trong trường hợp hiện tại, kết quả là hoàn toàn vô nghĩa:

Create Or Replace Function ExampleTable_Update (id bigint, value1 text) Returns 
...
    Update ExampleTable
    Set Value1 = value1
    Where id = id
    Returning Value1, Value2;
...
$$ Language SQL;

Mặc dù bạn có vẻ mong đợi rằng phiên bản thứ hai của id sẽ tham chiếu đến tham số hàm, nó không. Tên cột xuất hiện đầu tiên trong phạm vi của một câu lệnh SQL, trường hợp thứ hai tham chiếu đến cột. dẫn đến một biểu thức luôn là true ngoại trừ các giá trị NULL trong id . Do đó, bạn sẽ cập nhật tất cả các hàng , có thể dẫn đến mất dữ liệu nghiêm trọng Điều tồi tệ hơn, bạn thậm chí có thể không nhận ra cho đến sau này, bởi vì hàm SQL sẽ trả về one hàng tùy ý như được xác định bởi RETURNING mệnh đề của hàm (trả về một hàng, không phải một tập hợp các hàng).

Trong trường hợp cụ thể này, bạn sẽ nhận được "may mắn", vì bạn cũng có value1 = value1 , ghi đè cột bằng giá trị tồn tại từ trước của nó, không làm gì một cách hiệu quả .. không có gì theo cách rất tốn kém (trừ khi trình kích hoạt làm điều gì đó). Bạn có thể bối rối khi lấy một hàng tùy ý với value1 không thay đổi kết quả là.

Vì vậy, đừng.

Tránh các xung đột đặt tên tiềm ẩn như thế này trừ khi bạn biết chính xác mình đang làm gì (rõ ràng là không đúng như vậy). Một quy ước mà tôi thích là thêm dấu gạch dưới cho tên tham số và biến trong các hàm, trong khi tên cột không bao giờ bắt đầu bằng dấu gạch dưới. Trong nhiều trường hợp, bạn chỉ có thể sử dụng tham chiếu vị trí để rõ ràng:$1 , $2 , ..., nhưng điều đó chỉ vượt qua một nửa của vấn đề. Bất kỳ phương pháp nào cũng tốt miễn là bạn tránh xung đột đặt tên . Tôi đề nghị:

CREATE OR REPLACE FUNCTION foo (_id bigint, _value1 text)
   RETURNS json AS
$func$
UPDATE tbl
SET    value1 = _value1
WHERE  id     = _id
RETURNING json_build_object('value1', value1, 'value2', value2);
$func$  LANGUAGE sql;

Cũng lưu ý rằng điều này trả về giá trị cột thực tế trong value1 sau UPDATE , có thể giống hoặc không giống với tham số đầu vào của bạn _value1 . Có thể có quy tắc cơ sở dữ liệu hoặc trình kích hoạt can thiệp ...



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Liên đoàn động TẤT CẢ truy vấn trong Postgres

  2. Kế hoạch thực thi cho các hàm trong PostgreSQL

  3. vấn đề bí danh cột postgres

  4. Thêm ràng buộc datetime vào chỉ mục một phần nhiều cột PostgreSQL

  5. Làm thế nào để lấy một đối tượng json làm cột trong postgresql?