web-dev-qa-db-ja.com

ONLY_FULL_GROUP_BYを削除する代わりに

MySQL 5.5からMySQL 5.7にアップグレードした後、いくつかのクエリでエラーが発生します。

エラー1055(42000):

SELECTリストの式#1がGROUP BY句になく、GROUP BY句の列に機能的に依存しない非集計列 'grocery.Product_Category.category_id'が含まれています。これはsql_mode = only_full_group_byと互換性がありません

私は調査を行い、問題の原因と解決方法を見つけました。基本的に、@@ sql_modeからONLY_FULL_GROUP_BYを削除するだけで、すべてが再び機能します。

しかし、それが正しい行動方針であるかどうか疑問に思いました。これに代わる方法はありますか、クエリを作成するより良い方法でしょうか?

これは私のケースです( http://sqlfiddle.com/#!9/6f1bd ):

2つのテーブルがあります(ここでは構造を簡略化していますが、基本的には同じです):製品とカテゴリ、および製品を複数のカテゴリに所属させるための多対多の関係テーブル。

SELECT * FROM Product;

+------------+---------+
| product_id | name    |
+------------+---------+
|          1 | Tomato  |
|          2 | Orange  |
|          3 | Banana  |
|          4 | Lettuce |
|          5 | Carrot  |
+------------+---------+
5 rows in set (0,00 sec)

SELECT * FROM Category;

+-------------+------------+
| category_id | name       |
+-------------+------------+
|           1 | Fruits     |
|           2 | Vegetables |
+-------------+------------+
2 rows in set (0,00 sec)

両方のカテゴリから製品を取得したいので、最も単純なクエリは次のようになります。

SELECT * FROM Product JOIN Product_Category USING(product_id)
  JOIN Category USING(category_id);

+-------------+------------+---------+------------+
| category_id | product_id | name    | name       |
+-------------+------------+---------+------------+
|           1 |          1 | Tomato  | Fruits     |
|           1 |          2 | Orange  | Fruits     |
|           1 |          3 | Banana  | Fruits     |
|           2 |          1 | Tomato  | Vegetables |
|           2 |          4 | Lettuce | Vegetables |
|           2 |          5 | Carrot  | Vegetables |
+-------------+------------+---------+------------+
6 rows in set (0,00 sec

しかし、商品が両方のカテゴリに存在する場合、一度だけ欲しいのですが、category_idが異なるため、DISTINCT選択を行っても役に立ちません。

SELECT DISTINCT * FROM Product JOIN Product_Category USING(product_id) JOIN Category USING(category_id);

+-------------+------------+---------+------------+
| category_id | product_id | name    | name       |
+-------------+------------+---------+------------+
|           1 |          1 | Tomato  | Fruits     |
|           1 |          2 | Orange  | Fruits     |
|           1 |          3 | Banana  | Fruits     |
|           2 |          1 | Tomato  | Vegetables |
|           2 |          4 | Lettuce | Vegetables |
|           2 |          5 | Carrot  | Vegetables |
+-------------+------------+---------+------------+
6 rows in set (0,00 sec)

MySQL 5.5では、product_idフィールドでGROUP BY句を使用しました。

SELECT * FROM Product JOIN Product_Category USING(product_id)
  JOIN Category USING(category_id) GROUP BY product_id;

+-------------+------------+---------+------------+
| category_id | product_id | name    | name       |
+-------------+------------+---------+------------+
|           1 |          1 | Tomato  | Fruits     |
|           1 |          2 | Orange  | Fruits     |
|           1 |          3 | Banana  | Fruits     |
|           2 |          4 | Lettuce | Vegetables |
|           2 |          5 | Carrot  | Vegetables |
+-------------+------------+---------+------------+
5 rows in set (0,00 sec)

重複を効果的に削除しました。結果は確定的ではないことはわかっていますが、トマトが果物または野菜のカテゴリに表示されているかどうかは気にせず、結果セットで1回だけ取得することが重要です。

しかし、MySQL 5.7でのこのクエリは、上記のエラーを引き起こします。

だから、私の質問は:@@ sql_modeからONLY_FULL_GROUP_BYを削除せずに同じ結果を得る別の(おそらくより良い)方法はありますか?

6
Sergio

GROUP BYに入れるために必要な列の数を最小限にする方法でクエリを書き直すことをお勧めします。あなたのケースでは、Product_Categoryテーブルのみにグループ化を適用することでそれを行うことができます。

例によると、そのテーブルには次のエントリがあります。

+------------+-------------+
| product_id | category_id |
+------------+-------------+
|          1 |           1 |
|          2 |           1 |
|          3 |           1 |
|          1 |           2 |
|          4 |           2 |
|          5 |           2 |
+------------+-------------+

出力内で製品名を一意にする必要があるため、このテーブルをproduct_idでグループ化し、category_idを選択します。製品あたりの最小値:

SELECT
  product_id,
  MIN(category_id) AS category_id
FROM
  Product_Category
GROUP BY
  product_id

これにより、次のような出力が得られます。

+------------+-------------+
| product_id | category_id |
+------------+-------------+
|          1 |           1 |
|          2 |           1 |
|          3 |           1 |
|          4 |           2 |
|          5 |           2 |
+------------+-------------+

各製品が1回だけリストされていることがわかります。そのテーブルを他の2つに結合しても、重複は生成されません。したがって、クエリのProduct_Categoryを上記のクエリを派生テーブルとして置き換えるだけです(もちろん、GROUP BYを削除します)。

SELECT
  *
FROM
  Product
  JOIN (
    SELECT
      product_id,
      MIN(category_id) AS category_id
    FROM
      Product_Category
    GROUP BY
      product_id
  ) AS pc USING(product_id)
  JOIN Category USING(category_id)
;
4
Andriy M