Tenho a seguinte estrutura de tabela em meu banco de dados Postgres: tabela chat_channel
possui uma coluna otherUserId
que pode ser unida com tabela a coluna userId
de outra tabela, profile
. A tabela profile
possui colunas userId
que podem ser unidas à coluna senderUserId
ou recipientUserId
na tabela note
.
Para qualquer linha que chat_channel
corresponda a determinados critérios de consulta, desejo extrair a única linha de profile
onde chat_channel."otherUserId" = profile."userId"
(como colunas extras retornadas da consulta). Também quero extrair colunas da linha correspondente note
para cada uma das duas userId
correspondências possíveis: where profile."userId" = note."senderUserId"
e where profile."userId" = note."recipientUserId"
.
Estou tendo problemas para descobrir como criar um alias adequado de colunas para essa consulta duplamente aninhada, visto que a consulta interna ingressa na mesma tabela duas vezes.
Tenho que criar um alias para as colunas, pois estou retornando colunas da mesma tabela duas vezes nas linhas de resultados. Além disso, as tabelas compartilham colunas com o mesmo nome (principalmente id
).
Tentativa (1):
SELECT "chat_channel"."id" "chat_channel.id",
"chat_channel"."channelId" "chat_channel.channelId",
"chat_channel"."userId" "chat_channel.userId",
"chat_channel"."otherUserId" "chat_channel.otherUserId",
"chat_channel"."sortByDateTime" "chat_channel.sortByDateTime",
"profile_other_user"."userId" "profile_other_user.userId",
"profile_other_user"."name" "profile_other_user.name",
"note_viewer_sent"."id" "note_viewer_sent.id", -- (a)
"note_viewer_sent"."senderUserId" "note_viewer_sent.senderUserId",
"note_viewer_sent"."recipientUserId" "note_viewer_sent.recipientUserId",
"note_viewer_sent"."noteText" "note_viewer_sent.noteText",
"note_viewer_received"."id" "note_viewer_received.id",
"note_viewer_received"."senderUserId" "note_viewer_received.senderUserId",
"note_viewer_received"."recipientUserId" "note_viewer_received.recipientUserId",
"note_viewer_received"."noteText" "note_viewer_received.noteText"
FROM "chat_channel"
LEFT JOIN LATERAL (
SELECT "profile_other_user"."id",
"profile_other_user"."userId",
"profile_other_user"."name"
FROM "profile" "profile_other_user"
LEFT JOIN LATERAL (
SELECT "note_viewer_sent"."id",
"note_viewer_sent"."senderUserId",
"note_viewer_sent"."recipientUserId",
"note_viewer_sent"."noteText"
FROM "note" "note_viewer_sent" -- (b)
WHERE "note_viewer_sent"."recipientUserId" = "profile_other_user"."userId"
AND "note_viewer_sent"."senderUserId" = $viewerUserId
LIMIT 1
) AS "note_viewer_sent" ON TRUE -- (c)
LEFT JOIN LATERAL (
SELECT "note_viewer_received"."id",
"note_viewer_received"."senderUserId",
"note_viewer_received"."recipientUserId",
"note_viewer_received"."noteText"
FROM "note" "note_viewer_received"
WHERE "note_viewer_received"."senderUserId" = "profile_other_user"."userId"
AND "note_viewer_received"."recipientUserId" = $viewerUserId
LIMIT 1
) AS "note_viewer_received" ON TRUE
WHERE "chat_channel"."otherUserId" = "profile_other_user"."userId"
LIMIT 1
) AS "profile_other_user" ON TRUE
WHERE "chat_channel"."userId" = $viewerUserId
ORDER BY "chat_channel"."sortByDateTime" DESC
Problemas com isso:
- Isso dá
ERROR: missing FROM-clause entry for table "note_viewer_sent"
na linha marcada-- (a)
. - Tentar criar um alias para a tabela (
-- (b)
) não resolve o problema. - Tentar criar um alias para os resultados da consulta mais profunda (
-- (c)
) não resolve o problema.
Tentativa (2):
No entanto, se eu fizer a FROM
cláusula de nível superior
FROM "chat_channel", "note" "note_viewer_sent", "note" "note_viewer_received"
então o erro não é mais gerado, mas as WHERE
cláusulas mais internas parecem ser ignoradas:
WHERE "note_viewer_sent"."recipientUserId" = "profile_other_user"."userId"
AND "note_viewer_sent"."senderUserId" = $viewerUserId
Especificamente, cada linha do conjunto de resultados contém colunas da mesma linha (errada) de notes
, para a qual a WHERE
condição nem sequer é válida.
Além disso, parece que o efeito padrão de colocar várias tabelas FROM
é obter um produto cruzado das tabelas. (Não posso reproduzir isso agora, mas a certa altura eu estava recebendo uma cópia de cada linha de profile
para cada linha de chat_channel
e as LIMIT 1
consultas internas estavam sendo ignoradas.)
Alguma idéia sobre o que estou fazendo de errado aqui?
A causa imediata da mensagem de erro é que você não pode fazer referência a nomes de colunas de saída de subconsultas aninhadas na
SELECT
lista externa. Poderia ser corrigido como:Ou melhor:
Isso passa por todas as colunas da
FROM
lista da subconsulta. Você deve distribuir aliases de coluna exclusivos nas subconsultas aninhadas, é claro, e referenciar o alias da tabelap
naSELECT
lista externa.Há uma lista de outros problemas aqui ...
Use exclusivamente identificadores legais, em letras minúsculas e sem aspas, para que você não precise usar aspas duplas. Ver:
Em particular, nunca inclua pontos nos identificadores entre aspas. Isso é realmente tentar tornar sua vida difícil.
Não omita a
AS
palavra-chave para aliases de coluna.Mas não há problema em omitir a
AS
palavra-chave para aliases de tabela.Você entende o contrário. Relacionado:
LIMIT
sem determinísticoORDER BY
retorna linhas arbitrárias. Não está claro se você deseja isso ou se precisa mesmoLIMIT
para começar.Pelo menos um nível
LATERAL
é supérfluo.Se a coluna puder ser nula, você provavelmente
NULLS LAST
desejaráVer:
O problema com nomes de colunas duplicados é agravado pelo antipadrão generalizado de usar "id" como nome de coluna em todas as tabelas. Não desaconselho, mas essa é apenas a minha opinião, ao contrário dos itens acima, que não são uma questão de opinião - na minha opinião. :) Mais sobre isso: