De acordo com o SQL padrão UNION
/ UNION ALL
não garante nenhuma ordem de classificação específica sem uma ORDER BY
cláusula externa - como quase não há lugar no SQL onde a ordem de classificação é garantida sem ORDER BY
.
No entanto, o Postgres usa uma etapa "Append" para casos simples de UNION ALL
, portanto, os resultados da primeira perna (mesmo que não classificados entre suas partições) sempre vêm antes da próxima perna, etc. O Postgres simplesmente anexa os resultados de cada perna na ordem dada. Isso é particularmente relevante com uma LIMIT
cláusula:
SELECT 1 FROM tbl -- or any complex query
UNION ALL
SELECT 2
LIMIT 1
Obviamente, isso não vale para UNION
(sem ALL
). Mas fora isso, nunca vi o Postgres retornar fora de ordem, ou seja, '2' da consulta acima, enquanto o primeiro SELECT
também retornaria linha(s). Nem mesmo se a primeira etapa for extremamente cara.
Eu baseei consultas sobre esse comportamento no passado. Agora , encontrei uma alegação de que o Postgres pode retornar linhas fora de ordem aqui, mas sem comprovação real.
O atual manual do Postgres tem a dizer sobre o assunto:
UNION
efetivamente acrescenta o resultado dequery2
ao resultado dequery1
(embora não haja garantia de que esta seja a ordem em que as linhas são realmente retornadas). Além disso, elimina linhas duplicadas de seu resultado, da mesma forma queDISTINCT
, a menos queUNION ALL
seja usado.
Isso é bastante obscuro. A ordem citada se aplica à lista de SELECT
cláusulas, ou linhas dentro de cada cláusula, ou apenas ao conjunto retornado? Além disso, UNION ALL
é mencionado apenas na segunda frase, então não está claro se a primeira frase tão importante deve se aplicar a UNION ALL
...
Alguém pode mostrar um exemplo, onde as linhas são retornadas fora de ordem, quebrando a sequência de UNION ALL
cláusulas? Em qualquer versão do Postgres. (Mesmo que a versão mais recente seja a mais interessante.)
Se não for isso, há motivos para acreditar que isso pode mudar em uma versão futura?
ORDER BY
não é a questão imediata aqui. A questão é se várias UNION ALL
cláusulas retornam linhas na sequência determinada (antes LIMIT
podem ser acionadas e impedir que outras ramificações sejam executadas).
Houve uma pergunta semelhante recentemente na lista de discussão pgsql-docs,
Esclareça as garantias de ordenação na combinação de consultas (ou falta delas) :
Tom Lane (e outros) respondeu:
Foi estabelecido, não há garantia de que as linhas do primeiro
UNION ALL
termo sejam retornadas antes das linhas do próximoUNION ALL
termo etc. Nem no SQL padrão, nem no Postgres.A solução limpa é não confiar na ordem das cláusulas.
Alternativas
Para consultas simples como mostrado acima (sem exterior
ORDER BY
ouJOIN
) a sequência foi observada de qualquer maneira até a adição deParallel Append
planos paralelos com o Postgres 11 . Simplesmente porqueAppend
era a única opção de plano - que é (foi) implementada dessa maneira.Ainda funciona no Postgres 15 atual , desde que
Parallel Append
não esteja envolvido - o que só acontece para grandes conjuntos. Você pode ter certeza disso desativando essa opção. O manual:Você pode definir essa opção localmente, mesmo apenas para a transação atual corrigir o código antigo rapidamente com:
Não se prenda a essa solução provisória. Melhor corrigir seu código SQL corretamente.
Alternativas adequadas
vladic comentou:
A próxima melhor coisa que posso pensar é uma função PL/pgSQL construindo o conjunto. Você pode verificar o número de linhas de resultados após cada consulta com
GET DIAGNOSTICS
, modificar (reduzir) oLIMIT
para a próxima consulta eRETURN
assim que encontrar o suficiente. Pode ser feito sem SQL dinâmico (portanto, não éEXECUTE
necessário), poisLIMIT
aceita um parâmetro. Ver:Consideravelmente mais código. Mais despesas de planejamento. Mas pelo lado positivo: o adaptado dinamicamente
LIMIT
pode até produzir melhores planos de consulta para consultas subsequentes.Outro exemplo, repetindo a mesma consulta em um loop, também sem SQL dinâmico:
Exemplo de código para uma dinâmica
LIMIT
enquanto também usa SQL dinâmicoEXECUTE
em um loop: