Cập nhật:Trong PostgreSQL 9.4, điều này được cải thiện rất nhiều với sự ra đời của to_json
, json_build_object
, json_object
và json_build_array
, mặc dù nó dài dòng do cần phải đặt tên cho tất cả các trường một cách rõ ràng:
select
json_build_object(
'id', u.id,
'name', u.name,
'email', u.email,
'user_role_id', u.user_role_id,
'user_role', json_build_object(
'id', ur.id,
'name', ur.name,
'description', ur.description,
'duty_id', ur.duty_id,
'duty', json_build_object(
'id', d.id,
'name', d.name
)
)
)
from users u
inner join user_roles ur on ur.id = u.user_role_id
inner join role_duties d on d.id = ur.duty_id;
Đối với các phiên bản cũ hơn, hãy đọc tiếp.
Nó không giới hạn ở một hàng duy nhất, nó chỉ hơi đau một chút. Bạn không thể đặt bí danh cho các kiểu hàng tổng hợp bằng cách sử dụng AS
, vì vậy bạn cần sử dụng biểu thức truy vấn con bí danh hoặc CTE để đạt được hiệu quả:
select row_to_json(row)
from (
select u.*, urd AS user_role
from users u
inner join (
select ur.*, d
from user_roles ur
inner join role_duties d on d.id = ur.duty_id
) urd(id,name,description,duty_id,duty) on urd.id = u.user_role_id
) row;
sản xuất, thông qua http://jsonprettyprint.com/:
{
"id": 1,
"name": "Dan",
"email": "[email protected]",
"user_role_id": 1,
"user_role": {
"id": 1,
"name": "admin",
"description": "Administrative duties in the system",
"duty_id": 1,
"duty": {
"id": 1,
"name": "Script Execution"
}
}
}
Bạn sẽ muốn sử dụng array_to_json(array_agg(...))
khi bạn có mối quan hệ 1:nhiều, btw.
Lý tưởng nhất là truy vấn trên có thể được viết là:
select row_to_json(
ROW(u.*, ROW(ur.*, d AS duty) AS user_role)
)
from users u
inner join user_roles ur on ur.id = u.user_role_id
inner join role_duties d on d.id = ur.duty_id;
... nhưng ROW
của PostgreSQL hàm tạo không chấp nhận AS
bí danh cột. Thật đáng buồn.
Rất may, họ tối ưu hóa giống nhau. So sánh các kế hoạch:
- Phiên bản truy vấn con lồng nhau; so với
-
ROW
lồng nhau sau phiên bản phương thức khởi tạo với bí danh bị xóa để nó thực thi
Vì CTE là hàng rào tối ưu hóa, nên việc diễn đạt lại phiên bản truy vấn con lồng nhau để sử dụng CTE chuỗi (WITH
biểu thức) cũng có thể không hoạt động và sẽ không dẫn đến cùng một kế hoạch. Trong trường hợp này, bạn đang gặp khó khăn với các truy vấn con lồng nhau xấu xí cho đến khi chúng tôi nhận được một số cải tiến đối với row_to_json
hoặc một cách để ghi đè tên cột trong ROW
hàm tạo trực tiếp hơn.
Nói chung, nguyên tắc là nơi bạn muốn tạo một đối tượng json với các cột a, b, c
và bạn ước mình có thể viết cú pháp không hợp lệ:
ROW(a, b, c) AS outername(name1, name2, name3)
thay vào đó, bạn có thể sử dụng truy vấn con vô hướng trả về giá trị được nhập theo hàng:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
Hoặc:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
Ngoài ra, hãy nhớ rằng bạn có thể soạn json
các giá trị mà không cần trích dẫn bổ sung, ví dụ:nếu bạn đặt đầu ra của một json_agg
trong một row_to_json
, json_agg
bên trong kết quả sẽ không được trích dẫn dưới dạng một chuỗi, nó sẽ được kết hợp trực tiếp dưới dạng json.
ví dụ. trong ví dụ tùy ý:
SELECT row_to_json(
(SELECT x FROM (SELECT
1 AS k1,
2 AS k2,
(SELECT json_agg( (SELECT x FROM (SELECT 1 AS a, 2 AS b) x) )
FROM generate_series(1,2) ) AS k3
) x),
true
);
đầu ra là:
{"k1":1,
"k2":2,
"k3":[{"a":1,"b":2},
{"a":1,"b":2}]}
Lưu ý rằng json_agg
product, [{"a":1,"b":2}, {"a":1,"b":2}]
, đã không được thoát lại, dưới dạng text
sẽ là.
Điều này có nghĩa là bạn có thể soạn các hoạt động json để tạo các hàng, bạn không phải lúc nào cũng phải tạo các kiểu kết hợp PostgreSQL cực kỳ phức tạp, sau đó gọi row_to_json
trên đầu ra.