PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Convertendo consultas SELECT DISTINCT ON do Postgresql para MySQL


Não há um equivalente exato para converter uma consulta Postgresql que use SELECT DISTINCT ON para MySQL.

Postgresql SELECT DISTINCT ON

No Postgresql, a consulta a seguir eliminará todas as linhas onde as expressões (col1, col2, col3) match, e manterá apenas a "primeira col4, col5 row" para cada conjunto de linhas correspondentes:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename

Então, se sua tabela estiver assim:
col1 | col2 | col3 | col4 | col5
--------------------------------
1    | 2    | 3    | 777  | 888
1    | 2    | 3    | 888  | 999
3    | 3    | 3    | 555  | 555

nossa consulta manterá apenas uma linha para (1,2,3) e uma linha para (3,3,3). As linhas resultantes serão então:
col4 | col5
-----------
777  | 888
555  | 555

observe que a "primeira linha" de cada conjunto é imprevisível, nossa primeira linha também pode ser (888, 999), a menos que especifiquemos um ORDER BY:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename
ORDER BY col1, col2, col3, col4

(o DISTINCT nas expressões deve corresponder às expressões ORDER BY mais à esquerda, mas o ORDER BY pode conter expressões adicionais).

Extensão MySQL para GROUP BY

O MySQL estende o uso de GROUP BY para que possamos selecionar colunas não agregadas não nomeadas na cláusula GROUP BY. Sempre que selecionamos colunas não agregadas o servidor fica livre para escolher qualquer valor de cada grupo daquela coluna, portanto os valores resultantes serão indeterminados.

Então esta consulta Postgresql:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename

pode ser considerado equivalente a esta consulta MySQL:
SELECT col4, col5
FROM tablename
GROUP BY col1, col2, col3

tanto o Postgresql quanto o MySQL retornarão a "Primeira linha" para cada um (col1, col2, col3), e em ambos os casos a linha retornada é imprevisível porque não especificamos e ordenamos por cláusula.

Muitas pessoas ficariam muito tentadas a converter esta consulta do Postgresql com um ORDER BY:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename
ORDER BY col1, col2, col3, col4

com este:
SELECT col4, col5
FROM (
  SELECT col1, col2, col3, col4, col5
  FROM tablename
  ORDER BY col1, col2, col3, col4
) s
GROUP BY col1, col2, col3

a idéia aqui é aplicar um ORDER BY a uma subconsulta para que quando o MySQL agrupar por col1, col2, col3 ele mantenha o primeiro valor encontrado para col4 e col5. A ideia é boa, mas está errada! O MySQL é livre para escolher qualquer valor para col4 e col5, e não sabemos quais são os primeiros valores encontrados, depende do otimizador. Então eu corrigiria para isso:
SELECT t1.col4, t1.col5
FROM tablename t1 INNER JOIN (SELECT col1, col2, col3, MIN(col4) as m_col4
                              FROM tablename
                              GROUP BY col1, col2, col3) s
     ON t1.col1=s.col1
        AND t1.col2=s.col2
        AND t1.col3=s.col3
        AND t1.col4=s.m_col4
GROUP BY
  t1.col1, t1.col2, t1.col3, t1.col4

mas isso está começando a ficar mais complicado.

Conclusão

Como regra geral, não há uma maneira exata de converter uma consulta Postgresql em uma consulta MySQL, mas existem muitas soluções alternativas, a consulta resultante pode ser tão simples quanto a original ou pode se tornar muito complicada, mas depende de a própria consulta.