web-dev-qa-db-ja.com

GROUP BYはMIN()集約関数で誤った結果を出します

次のようなテーブルがあります。

+ ------ + -------- + ---------- + ------ + -------- + ------ -+ ------ + ------------ + 
 | id |コード|カテゴリ| mq |体重|織り|ショー| min(price)| 
 + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + 
 | 1 | DT450R |カーボン| 1 | 450 |プレーン| 1 | 90 | 
 | 2 | DT450R |カーボン| 2 | 450 |プレーン| 1 | 40 | 
 | 3 | DT450R |カーボン| 5 | 450 |プレーン| 1 | 75 | 
 | 7 | PP120Q |カーボン| 3 | 120 |ツイル| 1 | 28 | 
 | 8 | PP120Q |カーボン| 7 | 120 |ツイル| 1 | 65 | 
 | 9 | PP120Q |カーボン| 9 | 120 |ツイル| 1 | 49 | 
 | 4 | ZX300R |カーボン| 1 | 300 |プレーン| 0 | 12 | 
 | 5 | ZX300R |カーボン| 15 | 300 |プレーン| 1 | 128 | 
 | 6 | ZX300R |カーボン| 30 | 300 |プレーン| 1 | 92 | 
 + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ + 

ここに sqlfiddle を作成しました。

各コードの表から最小価格が必要です。次のクエリを使用してみました。

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

なぜグループは間違った結果を得るのですか? id = 1 の代わりに id =2

誤った出力:

+ ------ + -------- + ---------- + ------ + -------- + ------ -+ ------ + ------------ + 
 | id |コード|カテゴリ| mq |体重|織り|ショー| min(price)| 
 + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + 
 | 1 | DT450R |カーボン| 1 | 450 |プレーン| 1 | 40 | 
 | 7 | PP120Q |カーボン| 3 | 120 |ツイル| 1 | 28 | 
 | 4 | ZX300R |カーボン| 1 | 300 |プレーン| 0 | 12 | 
 + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ + 

期待される出力:

+ ------ + -------- + ---------- + ------ + -------- + ------ -+ ------ + ------------ + 
 | id |コード|カテゴリ| mq |体重|織り|ショー| min(price)| 
 + ------ + -------- + ---------- + ------ + ----- --- + ------- + ------ + ------------ + 
 | 2 | DT450R |カーボン| 2 | 450 |プレーン| 1 | 40 | 
 | 4 | ZX300R |カーボン| 1 | 300 |プレーン| 0 | 12 | 
 | 7 | PP120Q |カーボン| 3 | 120 |ツイル| 1 | 28 | 
 + ------ + -------- + ---------- + ------ + -------- + ------- + ------ + ------------ +
6
denny

MySQL DBAとして、私は悲しいことにMySQLがそのSQL処理で cavalier になる可能性があることを認めます。これの最も悪名高い偉業の1つはそのGROUP BY動作。

例として、Aaron Bertrandが投稿に回答しました なぜSQLクエリでGroup by 1とGroup by 1,2,3を使用するのですか? ここで、MySQLのGROUP BY なので cowboy who-knows-what-will-happen grouping同意する必要がありました

提案

GROUP BYcodeを使用

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

3つのことを行う

  1. クエリをサブクエリにする
  2. priceでは、代わりにtotalをエイリアスとして使用します
  3. codepriceの製品テーブルに再び結合します。

ここに提案されたクエリがあります

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

または

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;

チェックアウト SQL Fiddle for this

試してみる !!!

2017-01-06 16:17 EST更新

特定のコードに対して同じ最低価格の行が複数存在する場合は、クエリを実行してサブクエリにし、結合して各(codeprice)の最小IDを取得し、productによって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);

チェックアウト SQL Fiddle for that)

11
RolandoMySQLDBA

これでうまくいくはずです:

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 ;

注意:tiesがある場合(つまり、min(price)はグループごとに複数の行に表示されます)、すべての行が返されます。引き分けの場合に別の動作が必要な場合、状況は少し複雑になります... 2番目の選択肢の基準が必要です(可能であれば、別の引き分けを取得できない基準)、および別のレベルのsubquerying

このクエリも確認できます@ SQLFiddle

すべての@RolandoMySQLDBAの説明をチェックして、GROUP BY mySQLで。それは簡単にトリッキーになる可能性があります。

5
joanolo

質問は古いですが、さまざまなアプローチで回答が見つからなかったので、(結合を使用せずに)より簡単な代替案で応答することにしました。

グループ化により、GROUP BYステートメントに記録されたものと同じ値を持つレコードが統合されます。 MySQLがこれを行うとき、維持するレコードを選択します(非決定的)。彼が1つの行を保持し、他の行を破棄し、MIN命令の値を保持する、グループが完了するまで続ける、などを想像してください。

いくつかのテストでは、MariaDBが最後の行を保持しながら、MySQLは常に最初の行の値を選択しました。 doc にはこれへの参照はなく、サーバーが各グループの任意の値を自由に選択できることを示しているだけです。

これを解決するには、一時テーブル(またはサブクエリ)を作成し、保持されるレコード(上または下)とグループ化される列に従って、ASCまたはDESCのMINで使用される列を並べ替えます。次に、選択時に、MINコマンドでグループ化を使用します。

これを見てくださいフィドル

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