Atualização:No PostgreSQL 9.4 isso melhora muito com a introdução do
to_json
, json_build_object
, json_object
e json_build_array
, embora seja detalhado devido à necessidade de nomear todos os campos explicitamente: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;
Para versões mais antigas, continue lendo.
Não se limita a uma única linha, é apenas um pouco doloroso. Você não pode criar alias para tipos de linha compostos usando
AS
, então você precisa usar uma expressão de subconsulta com alias ou CTE para obter o efeito: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;
produz, via 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"
}
}
}
Você vai querer usar
array_to_json(array_agg(...))
quando você tem um relacionamento 1:muitos, btw. A consulta acima deve, idealmente, ser escrita como:
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;
... mas a
ROW
do PostgreSQL construtor não aceita AS
aliases de coluna. Infelizmente. Felizmente, eles otimizam o mesmo. Compare os planos:
- A versão da subconsulta aninhada; vs
- O último aninhado
ROW
versão do construtor com os aliases removidos para que seja executado
Como os CTEs são cercas de otimização, reformular a versão da subconsulta aninhada para usar CTEs encadeados (
WITH
expressões) pode não funcionar tão bem e não resultará no mesmo plano. Nesse caso, você está preso a subconsultas aninhadas feias até obtermos algumas melhorias em row_to_json
ou uma maneira de substituir os nomes das colunas em um ROW
construtor mais diretamente. De qualquer forma, em geral, o princípio é que onde você deseja criar um objeto json com colunas
a, b, c
, e você gostaria de escrever a sintaxe ilegal:ROW(a, b, c) AS outername(name1, name2, name3)
em vez disso, você pode usar subconsultas escalares que retornam valores digitados por linha:
(SELECT x FROM (SELECT a AS name1, b AS name2, c AS name3) x) AS outername
Ou:
(SELECT x FROM (SELECT a, b, c) AS x(name1, name2, name3)) AS outername
Além disso, lembre-se de que você pode compor
json
valores sem cotação adicional, por ex. se você colocar a saída de um json_agg
dentro de um row_to_json
, o json_agg
interno result não será citado como uma string, ele será incorporado diretamente como json. por exemplo. no exemplo arbitrário:
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
);
a saída é:
{"k1":1,
"k2":2,
"k3":[{"a":1,"b":2},
{"a":1,"b":2}]}
Observe que o
json_agg
produto, [{"a":1,"b":2}, {"a":1,"b":2}]
, não foi escapado novamente, pois text
seria. Isso significa que você pode compor json para construir linhas, você nem sempre precisa criar tipos compostos PostgreSQL extremamente complexos e chamar
row_to_json
na saída.