J'utilise PostgreSQL et migre maintenant vers MySQL.
Dans mes requêtes, j'utilise PostgreSQL's SELECT DISTINCT ON (col1, col2, col3)
, je me demandais s'il y avait une contrepartie de cette déclaration dans MySQL.
Il n'y a pas d'équivalent exact pour convertir une requête Postgresql qui utilise SELECT DISTINCT ON en MySQL.
Postgresql SELECT DISTINCT ON
Dans Postgresql, la requête suivante éliminera toutes les lignes où les expressions (col1, col2, col3)
correspond, et il ne conservera que la "première ligne col4, col5" pour chaque ensemble de lignes correspondantes:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename
Donc, si votre table est comme ça:
col1 | col2 | col3 | col4 | col5
--------------------------------
1 | 2 | 3 | 777 | 888
1 | 2 | 3 | 888 | 999
3 | 3 | 3 | 555 | 555
notre requête gardera une seule ligne pour (1,2,3) et une ligne pour (3,3,3). Les lignes résultantes seront alors:
col4 | col5
-----------
777 | 888
555 | 555
veuillez noter que la "première ligne" de chaque ensemble est imprévisible, notre première ligne peut également être (888, 999), sauf si nous spécifions un ORDER BY:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename
ORDER BY col1, col2, col3, col4
(les expressions DISTINCT on doivent correspondre aux expressions ORDER BY les plus à gauche, mais ORDER BY peut contenir des expressions supplémentaires).
Extension MySQL vers GROUP BY
MySQL étend l'utilisation de GROUP BY afin que nous puissions sélectionner des colonnes non agrégées non nommées dans la clause GROUP BY. Chaque fois que nous sélectionnons des colonnes non agrégées, le serveur est libre de choisir n'importe quelle valeur de chaque groupe de cette colonne, de sorte que les valeurs résultantes seront indéterminées.
Donc, cette requête Postgresql:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename
peut être considéré comme équivalent à cette requête MySQL:
SELECT col4, col5
FROM tablename
GROUP BY col1, col2, col3
postgresql et MySQL renverront la "première ligne" pour chacun (col1, col2, col3), et dans les deux cas, la ligne retournée est imprévisible car nous n'avons pas spécifié et ordonné par clause.
Beaucoup de gens seraient très tentés de convertir cette requête Postgresql avec un ORDER BY:
SELECT DISTINCT ON (col1, col2, col3) col4, col5
FROM tablename
ORDER BY col1, col2, col3, col4
avec celui-ci:
SELECT col4, col5
FROM (
SELECT col1, col2, col3, col4, col5
FROM tablename
ORDER BY col1, col2, col3, col4
) s
GROUP BY col1, col2, col3
l'idée ici est d'appliquer un ORDER BY à une sous-requête afin que lorsque MySQL regroupe par col1, col2, col3, il conserve la première valeur rencontrée pour col4 et col5. L'idée est bonne, mais c'est faux! MySQL est libre de choisir n'importe quelle valeur pour col4 et col5, et nous ne savons pas quelles sont les premières valeurs rencontré, cela dépend de l'optimiseur. Je le corrigerais donc:
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
mais cela commence à devenir plus compliqué.
Conclusion
En règle générale, il n'y a pas de moyen exact de convertir une requête Postgresql en une requête MySQL, mais il existe de nombreuses solutions de contournement, la requête résultante peut être aussi simple que celle d'origine ou elle peut devenir très compliquée, mais cela dépend de la requête elle-même.
Utilisez une sous-requête pour déterminer l'ordre et une requête externe pour les regrouper.
Comme le souligne @a_horse_with_no_name, cela fonctionne parce que MySQL autorise group by
, contrairement aux autres SGBD.
Par exemple:
CREATE TABLE customer_order
(`customer` varchar(5), `item` varchar(6), `date` datetime)
;
INSERT INTO customer_order
(`customer`, `item`, `date`)
VALUES
('alice', 'widget', '2000-01-05 00:00:00'),
('bob', 'widget', '2000-01-02 00:00:00'),
('alice', 'widget', '2000-01-01 00:00:00'),
('alice', 'wodget', '2000-01-06 00:00:00')
;
Requête pour la première commande de chaque client:
select *
from
(select customer, item, date
from customer_order
order by date) c
group by customer
Résultat:
| CUSTOMER | ITEM | DATE |
|----------|--------|--------------------------------|
| alice | widget | January, 01 2000 00:00:00+0000 |
| bob | widget | January, 02 2000 00:00:00+0000 |