it-swarm.com.de

GROUP BY liefert mit der MIN () - Aggregatfunktion ein falsches Ergebnis

Ich habe einen Tisch, der so aussieht:

+ ------ + -------- + ---------- + ------ + -------- + ------ - + ------ + ------------ + 
 | id | Code | Kategorie | mq | Gewicht | weben | show | min (Preis) | 
 + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + 
 | 1 | DT450R | Kohlenstoff | 1 | 450 | schlicht | 1 | 90 | 
 | 2 | DT450R | Kohlenstoff | 2 | 450 | schlicht | 1 | 40 | 
 | 3 | DT450R | Kohlenstoff | 5 | 450 | schlicht | 1 | 75 | 
 | 7 | PP120Q | Kohlenstoff | 3 | 120 | Köper | 1 | 28 | 
 | 8 | PP120Q | Kohlenstoff | 7 | 120 | Köper | 1 | 65 | 
 | 9 | PP120Q | Kohlenstoff | 9 | 120 | Köper | 1 | 49 | 
 | 4 | ZX300R | Kohlenstoff | 1 | 300 | schlicht | 0 | 12 | 
 | 5 | ZX300R | Kohlenstoff | 15 | 300 | schlicht | 1 | 128 | 
 | 6 | ZX300R | Kohlenstoff | 30 | 300 | schlicht | 1 | 92 | 
 + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ + 

Ich habe hier ein sqlfiddle erstellt.

Ich möchte den Mindestpreis aus der Tabelle in jedem Code. Ich habe versucht, die folgende Abfrage zu verwenden:

select id, code, category, mq, weight, weave, price, `show`, min(price) as total 
from product group by code;

Warum erhält die Gruppe das falsche Ergebnis? Es kehrt zurück id = 1 Anstatt von id =2.

Falsche Ausgabe:

+ ------ + -------- + ---------- + ------ + -------- + ------ - + ------ + ------------ + 
 | id | Code | Kategorie | mq | Gewicht | weben | show | min (Preis) | 
 + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + 
 | 1 | DT450R | Kohlenstoff | 1 | 450 | schlicht | 1 | 40 | 
 | 7 | PP120Q | Kohlenstoff | 3 | 120 | Köper | 1 | 28 | 
 | 4 | ZX300R | Kohlenstoff | 1 | 300 | schlicht | 0 | 12 | 
 + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ + 

Erwartete Ausgabe:

+ ------ + -------- + ---------- + ------ + -------- + ------ - + ------ + ------------ + 
 | id | Code | Kategorie | mq | Gewicht | weben | show | min (Preis) | 
 + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + 
 | 2 | DT450R | Kohlenstoff | 2 | 450 | schlicht | 1 | 40 | 
 | 4 | ZX300R | Kohlenstoff | 1 | 300 | schlicht | 0 | 12 | 
 | 7 | PP120Q | Kohlenstoff | 3 | 120 | Köper | 1 | 28 | 
 + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ +
6
denny

Als MySQL-DBA gebe ich leider zu, dass MySQL in seiner SQL-Verarbeitung eher Cavalier sein kann. Eine der berüchtigtsten Leistungen ist das Verhalten von GROUP BY.

Als Beispiel antwortete Aaron Bertrand auf den Beitrag Warum verwenden wir Group by 1 und Group by 1,2,3 in der SQL-Abfrage? wo er MySQLs GROUP BY Als cowboy who-knows-what-will-happen grouping. Ich musste nur zustimmen .

VORSCHLAG

Schreiben Sie den GROUP BY Mit code neu.

select code,min(price) as total 
from product group by code

Mach drei Dinge

  1. Machen Sie die Abfrage zu einer Unterabfrage
  2. Verwenden Sie stattdessen price als Alias ​​für total.
  3. Fügen Sie es wieder zur Produkttabelle unter code und price hinzu.

Hier ist die vorgeschlagene Abfrage

select b.* from
(select code,min(price) as price from product group by code) a
inner join product b using (code,price);

oder

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;

Kasse das SQL Fiddle dafür

VERSUCHE ES !!!

UPDATE 2017-01-06 16:17 EST

Wenn für einen bestimmten Code mehr als eine Zeile mit demselben Mindestpreis vorhanden ist, müssen Sie die Abfrage übernehmen, sie zu einer Unterabfrage machen und sie verbinden, um die Mindest-ID für jede Zeile abzurufen (code, price) und verbinde das mit product mit 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);

Kasse das SQL Fiddle dafür

11
RolandoMySQLDBA

Das sollte den Trick machen:

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 ;

Vorsichtsmaßnahme: Wenn es Bindungen gibt (dh: der min (Preis) erscheint in mehr als einer Zeile pro Gruppe), Alle Zeilen werden zurückgegeben . Wenn Sie im Falle eines Unentschieden ein anderes Verhalten wünschen, werden die Dinge etwas komplizierter ... Es wird ein Kriterium zweiter Wahl benötigt (wenn möglich eines, das kein weiteres Unentschieden erzielen kann) und eine andere Stufe von subquerying .

Sie können diese Abfrage auch @ SQLFiddle überprüfen

Sie können alle @ RolandoMySQLDBA-Erklärungen auf alle "nicht standardmäßigen" Dinge überprüfen, die hinter einem GROUP BY In mySQL stehen. Es kann leicht schwierig sein.

5
joanolo

Die Frage ist alt, aber da ich keine Antworten mit unterschiedlichen Ansätzen sah, entschied ich mich, mit einer einfacheren Alternative zu antworten (ohne Joins zu verwenden).

Die Gruppierung vereinheitlicht die Datensätze mit denselben Werten, die in der Anweisung GROUP BY aufgezeichnet wurden. Wenn MySQL dies tut, wählt es einen Datensatz aus, der gepflegt werden soll (nicht deterministisch). Stellen Sie sich vor, er hält eine Zeile und verwirft die anderen, wobei er nur den Wert der MIN-Anweisung beibehält, bis er eine Gruppe abgeschlossen hat, und so weiter.

In mehreren Tests hat MySQL immer die Werte der ersten Zeile ausgewählt, während MariaDB die letzte beibehalten hat. In doc gibt es keinen Hinweis darauf und es heißt nur, dass der Server frei ist, einen beliebigen Wert für jede Gruppe zu wählen.

Um dies zu lösen, erstellen Sie einfach eine temporäre Tabelle (oder Unterabläufe) und ordnen Sie die Spalte so an, dass sie mit MIN in ASC oder DESC verwendet wird, je nachdem, welcher Datensatz aufbewahrt wird (oben oder unten) und welche Spalte gruppiert werden soll. Verwenden Sie dann bei einer Auswahl die Gruppierung mit dem Befehl MIN.

Siehe dies Geige.

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;
0
dh117