J'ai une table qui ressemble à ceci:
+ ------ + -------- + ---------- + ------ + -------- + ------ - + ------ + ------------ + | id | code | catégorie | mq | poids | tisser | spectacle | min (prix) | + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + | 1 | DT450R | carbone | 1 | 450 | ordinaire | 1 | 90 | | 2 | DT450R | carbone | 2 | 450 | ordinaire | 1 | 40 | | 3 | DT450R | carbone | 5 | 450 | ordinaire | 1 | 75 | | 7 | PP120Q | carbone | 3 | 120 | sergé | 1 | 28 | | 8 | PP120Q | carbone | 7 | 120 | sergé | 1 | 65 | | 9 | PP120Q | carbone | 9 | 120 | sergé | 1 | 49 | | 4 | ZX300R | carbone | 1 | 300 | ordinaire | 0 | 12 | | 5 | ZX300R | carbone | 15 | 300 | ordinaire | 1 | 128 | | 6 | ZX300R | carbone | 30 | 300 | ordinaire | 1 | 92 | + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ +
J'ai créé un sqlfiddle ici.
Je veux le prix minimum de la table dans chaque code. J'ai essayé d'utiliser la requête suivante:
select id, code, category, mq, weight, weave, price, `show`, min(price) as total
from product group by code;
Pourquoi le groupe obtient-il un mauvais résultat? Il revient id = 1
au lieu de id =2
.
Sortie incorrecte:
+ ------ + -------- + ---------- + ------ + -------- + ------ - + ------ + ------------ + | id | code | catégorie | mq | poids | tisser | spectacle | min (prix) | + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + | 1 | DT450R | carbone | 1 | 450 | ordinaire | 1 | 40 | | 7 | PP120Q | carbone | 3 | 120 | sergé | 1 | 28 | | 4 | ZX300R | carbone | 1 | 300 | ordinaire | 0 | 12 | + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ +
Production attendue:
+ ------ + -------- + ---------- + ------ + -------- + ------ - + ------ + ------------ + | id | code | catégorie | mq | poids | tisser | spectacle | min (prix) | + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + | 2 | DT450R | carbone | 2 | 450 | ordinaire | 1 | 40 | | 4 | ZX300R | carbone | 1 | 300 | ordinaire | 0 | 12 | | 7 | PP120Q | carbone | 3 | 120 | sergé | 1 | 28 | + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ +
En tant que DBA MySQL, j'admets malheureusement que MySQL peut être plutôt cavalier dans son traitement SQL. L'un des exploits les plus tristement célèbres de ceci est son GROUP BY
comportement.
Par exemple, Aaron Bertrand a répondu au message Pourquoi utilisons-nous Group by 1 et Group by 1,2,3 dans la requête SQL? où il a décrit _ GROUP BY
comme cowboy who-knows-what-will-happen grouping
. je devais juste être d'accord .
Réecrit le GROUP BY
en utilisant code
select code,min(price) as total
from product group by code
Faites trois choses
price
comme alias à la place sur total
code
et price
Voici la requête proposée
select b.* from
(select code,min(price) as price from product group by code) a
inner join product b using (code,price);
ou
select b.* from
(select code,min(price) as price from product group by code) a
inner join product b ON a.code=b.code AND a.price=b.price;
Commander le SQL Fiddle for this
S'il existe plus d'une ligne avec le même prix minimum pour un code donné, vous devez prendre la requête, en faire une sous-requête, la joindre pour récupérer l'id minimum pour chacune (code
, price
) et joignez-le à product
par id
:
select bb.* from
(select a.code,a.price,min(b.id) id from
(select code,min(price) as price from product group by code) a
inner join product b using (code,price)
group by a.code,a.price) aa
inner join product bb using (id);
Commander le SQL Fiddle for that
Cela devrait faire l'affaire:
SELECT
p.*
FROM
product p
JOIN
(
SELECT
code, min(price) AS min_price
FROM
product
GROUP BY
code
) m ON p.code = m.code AND p.price = m.min_price
ORDER BY
p.id ;
Attention: s'il y a égalité (ie: le min (prix) apparaît dans plus d'une ligne par groupe), toutes les lignes seront retournées . Si, en cas d'égalité, vous souhaitez un autre comportement, les choses se compliquent un peu ... un deuxième critère de choix est nécessaire (si possible, celui qui ne peut pas obtenir une autre égalité), et un autre niveau de subquerying
.
Vous pouvez également vérifier cette requête @ SQLFiddle
Vous pouvez vérifier toutes les explications @RolandoMySQLDBA pour toutes les choses "non standard" qui se cachent derrière un GROUP BY
dans mySQL. Cela peut facilement être délicat.
La question est ancienne mais comme je n'ai pas vu de réponses avec des approches différentes, j'ai décidé de répondre avec une alternative plus simple (sans utiliser de jointures).
Le regroupement unifie les enregistrements avec les mêmes valeurs enregistrées dans l'instruction GROUP BY. Lorsque MySQL fait cela, il choisit un enregistrement à maintenir (non déterministe). Imaginez qu'il détient une ligne et rejette les autres, en conservant simplement la valeur de l'instruction MIN, jusqu'à ce qu'il termine un groupe, et ainsi de suite.
Dans plusieurs tests, MySQL a toujours choisi les valeurs de la première ligne tandis que MariaDB a gardé la dernière. Il n'y a aucune référence à cela dans le doc et il indique simplement que le serveur est libre de choisir n'importe quelle valeur pour chaque groupe.
Pour résoudre ce problème, créez simplement une table temporaire (ou des sous-requêtes), en ordonnant la colonne à utiliser avec MIN en ASC ou DESC, en fonction de l'enregistrement conservé (en haut ou en bas) et de la colonne à regrouper. Ensuite, sur une sélection, utilisez le regroupement avec la commande MIN.
Voir ceci violon.
CREATE TABLE product (`id` int, `code` varchar(6), `category` varchar(6), `mq` int, `weight` int, `weave` varchar(5), `price` int, `show` int);
INSERT INTO product
(`id`, `code`, `category`, `mq`, `weight`, `weave`, `price`, `show`)
VALUES
(1, 'DT450R', 'carbon', 1, 450, 'plain', 90, 1),
(2, 'DT450R', 'carbon', 2, 450, 'plain', 40, 1),
(3, 'DT450R', 'carbon', 5, 450, 'plain', 75, 1),
(4, 'ZX300R', 'carbon', 1, 300, 'plain', 12, 0),
(5, 'ZX300R', 'carbon', 15, 300, 'plain', 128, 1),
(6, 'ZX300R', 'carbon', 30, 300, 'plain', 92, 1),
(7, 'PP120Q', 'carbon', 3, 120, 'twill', 28, 1),
(8, 'PP120Q', 'carbon', 7, 120, 'twill', 65, 1),
(9, 'PP120Q', 'carbon', 9, 120, 'twill', 49, 1)
;
# RESOLUTION
# I have used non-temporary table due to the fiddle constraint.
CREATE table tbOrdened
select
id, `code`, category, mq, weight, weave, `show`, price
from product
order by price asc, `code`;
SELECT idforn, idativo, min(valor) AS valor from tbOrdened as tb1 GROUP BY idativo;