web-dev-qa-db-ja.com

サブクエリORDER BYはMySQL 5.6では機能しませんが、5.5では機能します

3つのテーブル(製品、価格、カテゴリ)を結合して、結合したテーブルから最新&最低価格の結果を取得しようとします。

MySQL 5.5で期待どおりの結果を得ることができましたが、5.6にアップグレードした後、サブクエリのORDER BYは無視されるようです。 ORDER BYを機能させるためにクエリを変更する方法は?

クエリは次のように想定されています。

  1. 指定されたカテゴリーID(例:カテゴリーID 488)の下にある製品を検索します。
  2. 各製品の最新の価格を見つけます。
  3. 同じ「マッチキー」を持つ製品をグループ化します(製品名からストップワードを削除して生成されたマッチキー。他のプログラミングによって生成されます)
  4. グループ化された各製品の最低価格を見つける
  5. 最低価格でグループ化された各製品情報を表示する

間違った結果: enter image description here

3つのテーブルの関係は次のとおりです。

  • 製品<->多対多<->カテゴリ
  • 製品-> 1対多->価格

テーブル情報:

mysql> show columns from products;
+----------------+------------------+------+-----+---------------------+-----------------------------+
| Field          | Type             | Null | Key | Default             | Extra                       |
+----------------+------------------+------+-----+---------------------+-----------------------------+
| id             | int(10) unsigned | NO   | PRI | NULL                | auto_increment              |
| name           | varchar(300)     | NO   | MUL | NULL                |                             |
| active         | tinyint(1)       | YES  |     | 1                   |                             |
| created_at     | timestamp        | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
| updated_at     | timestamp        | NO   |     | 0000-00-00 00:00:00 |                             |
| matchkey       | varchar(300)     | NO   | MUL | NULL                |                             |
| sku            | varchar(50)      | YES  | MUL | NULL                |                             |
+----------------+------------------+------+-----+---------------------+-----------------------------+

mysql> show columns from prices;
+---------------+---------------------+------+-----+---------------------+-----------------------------+
| Field         | Type                | Null | Key | Default             | Extra                       |
+---------------+---------------------+------+-----+---------------------+-----------------------------+
| id            | bigint(20) unsigned | NO   | PRI | NULL                | auto_increment              |
| price_selling | decimal(8,2)        | NO   | MUL | NULL                |                             |
| price_before  | decimal(8,2)        | NO   |     | 0.00                |                             |
| product_id    | int(10) unsigned    | NO   | MUL | NULL                |                             |
| created_at    | timestamp           | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
| updated_at    | timestamp           | NO   |     | 0000-00-00 00:00:00 |                             |
+---------------+---------------------+------+-----+---------------------+-----------------------------+

mysql> show columns from categories;
+------------------+------------------+------+-----+---------------------+-----------------------------+
| Field            | Type             | Null | Key | Default             | Extra                       |
+------------------+------------------+------+-----+---------------------+-----------------------------+
| id               | int(10) unsigned | NO   | PRI | NULL                | auto_increment              |
| parent_id        | int(11)          | YES  | MUL | NULL                |                             |
| lft              | int(11)          | YES  | MUL | NULL                |                             |
| rgt              | int(11)          | YES  | MUL | NULL                |                             |
| depth            | int(11)          | YES  |     | NULL                |                             |
| name             | varchar(255)     | NO   | MUL | NULL                |                             |
| active           | tinyint(1)       | YES  |     | 1                   |                             |
| created_at       | timestamp        | NO   |     | CURRENT_TIMESTAMP   | on update CURRENT_TIMESTAMP |
| updated_at       | timestamp        | NO   |     | 0000-00-00 00:00:00 |                             |
+------------------+------------------+------+-----+---------------------+-----------------------------+

mysql> show columns from category_product;
+--------------------+------------------+------+-----+---------+-------+
| Field              | Type             | Null | Key | Default | Extra |
+--------------------+------------------+------+-----+---------+-------+
| product_id         | int(10) unsigned | NO   | PRI | NULL    |       |
| category_id        | int(10) unsigned | NO   | PRI | NULL    |       |
+--------------------+------------------+------+-----+---------+-------+

MySQL 5.5で機能するクエリは次のとおりです。

SELECT 
    p3.pid AS id, category_product.product_id, p3.name, p3.priceId, 
    p3.product_id, p3.price_selling, p3.price_before, MIN(p3.price_selling) AS minprice,
    p3.matchkey, categories.url_key AS category_urlkey
FROM category_product
RIGHT JOIN (
            SELECT
                pp.priceId, pp.product_id, pp.price_selling, pp.price_before,
                pp.pid, pp.name, pp.matchkey
            FROM (
                    SELECT 
                        prices.id AS priceId, prices.product_id, prices.price_selling, prices.price_before,
                        products.id AS pid, products.name, products.matchkey
                    FROM prices
                    RIGHT JOIN products ON prices.product_id = products.id
                    WHERE prices.id = (SELECT MAX(p.id) FROM prices p WHERE p.product_id = prices.product_id)
            ) AS pp
            GROUP BY pp.pid
            ORDER BY pp.price_selling ASC
            ) AS p3 ON category_product.product_id = p3.pid
RIGHT JOIN categories ON categories.id = category_product.category_id AND categories.id = 488 
GROUP BY p3.matchkey 
ORDER BY p3.pid DESC;

テーブル構造とサンプルデータをテストに使用できます。

--
-- Table structure for table `products`
--
CREATE TABLE IF NOT EXISTS `products` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(300) COLLATE utf8_unicode_ci NOT NULL,
  `active` tinyint(1) DEFAULT '1',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `matchkey` varchar(300) COLLATE utf8_unicode_ci NOT NULL,
  `sku` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `products_sku_index` (`sku`),
  KEY `id` (`id`),
  KEY `matchkey` (`matchkey`),
  FULLTEXT KEY `name` (`name`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=60057 ;

--
-- Dumping data for table `products`
--
INSERT INTO `products` (`id`, `name`, `active`, `created_at`, `updated_at`, `matchkey`, `sku`) VALUES
(26195, 'Samsung Galaxy S3 Mini GT-I8200 Unlocked Cellphone, White, 8GB', 1, '2016-01-23 14:38:25', '0000-00-00 00:00:00', '8gb cellphone galaxy gt i8200 mini s3 samsung white', NULL),
(26425, 'Samsung Galaxy S3 Mini GT-i8200 Factory Unlocked Cellphone, International Version, 8GB, White', 1, '2016-01-23 14:38:28', '0000-00-00 00:00:00', '8gb cellphone galaxy gt i8200 mini s3 samsung white', NULL),
(26632, 'Samsung Galaxy S3 Mini GT-i8200 Factory Unlocked Cellphone, International Version, 8GB, White', 1, '2016-01-23 14:38:29', '0000-00-00 00:00:00', '8gb cellphone galaxy gt i8200 mini s3 samsung white', NULL),
(28212, 'Samsung Galaxy S3 Mini GT-I8200 Unlocked Cellphone, White, 8GB', 1, '2016-01-23 14:38:51', '0000-00-00 00:00:00', '8gb cellphone galaxy gt i8200 mini s3 samsung white', NULL),
(35111, 'Samsung Galaxy S3 Mini GT-i8200 Factory Unlocked Cellphone, International Version, 8GB, White', 1, '2016-01-23 14:39:03', '0000-00-00 00:00:00', '8gb cellphone galaxy gt i8200 mini s3 samsung white', NULL),
(37611, '[From U.S.A]Samsung Galaxy S3 Mini GT-i8200 Factory Unlocked Cellphone, International Version, 8GB, White', 1, '2016-01-23 14:39:29', '0000-00-00 00:00:00', '8gb cellphone galaxy gt i8200 mini s3 samsung white', NULL),
(60017, 'Samsung Galaxy S3 Mini GT-i8200 Factory Unlocked Cellphone, International Version, 8GB, White', 1, '2016-01-23 14:38:11', '0000-00-00 00:00:00', '8gb cellphone galaxy gt i8200 mini s3 samsung white', NULL);

--
-- Table structure for table `prices`
--
CREATE TABLE IF NOT EXISTS `prices` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `price_selling` decimal(8,2) NOT NULL,
  `price_before` decimal(8,2) NOT NULL DEFAULT '0.00',
  `product_id` int(10) unsigned NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  KEY `price_selling` (`price_selling`),
  KEY `id` (`id`),
  KEY `product_id` (`product_id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=114663 ;

--
-- Dumping data for table `prices`
--
INSERT INTO `prices` (`id`, `price_selling`, `price_before`, `product_id`, `created_at`, `updated_at`) VALUES
(33585, '1052.40', '0.00', 26195, '2016-01-22 09:53:02', '0000-00-00 00:00:00'),
(65594, '1052.40', '0.00', 26195, '2016-01-23 01:27:08', '0000-00-00 00:00:00'),
(110038, '1052.40', '0.00', 26195, '2016-01-23 14:38:25', '0000-00-00 00:00:00'),
(33815, '1052.40', '0.00', 26425, '2016-01-22 09:55:17', '0000-00-00 00:00:00'),
(65790, '1052.40', '0.00', 26425, '2016-01-23 01:27:11', '0000-00-00 00:00:00'),
(110209, '1016.40', '0.00', 26425, '2016-01-23 14:38:28', '0000-00-00 00:00:00'),
(34022, '1052.40', '0.00', 26632, '2016-01-22 09:57:43', '0000-00-00 00:00:00'),
(66020, '1052.40', '0.00', 26632, '2016-01-23 01:27:13', '0000-00-00 00:00:00'),
(110373, '1052.40', '0.00', 26632, '2016-01-23 14:38:29', '0000-00-00 00:00:00'),
(35602, '960.25', '0.00', 28212, '2016-01-22 10:12:50', '0000-00-00 00:00:00'),
(67396, '960.25', '0.00', 28212, '2016-01-23 01:27:45', '0000-00-00 00:00:00'),
(111555, '960.25', '0.00', 28212, '2016-01-23 14:38:51', '0000-00-00 00:00:00'),
(71346, '929.20', '0.00', 35111, '2016-01-23 01:37:05', '0000-00-00 00:00:00'),
(112318, '899.30', '0.00', 35111, '2016-01-23 14:39:03', '0000-00-00 00:00:00'),
(73852, '936.10', '0.00', 37611, '2016-01-23 01:54:47', '0000-00-00 00:00:00'),
(113711, '936.10', '0.00', 37611, '2016-01-23 14:39:29', '0000-00-00 00:00:00'),
(109601, '1108.99', '2369.91', 60017, '2016-01-23 14:38:08', '0000-00-00 00:00:00');


--
-- Table structure for table `categories`
--
CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `parent_id` int(11) DEFAULT NULL,
  `lft` int(11) DEFAULT NULL,
  `rgt` int(11) DEFAULT NULL,
  `depth` int(11) DEFAULT NULL,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `url_key` varchar(200) COLLATE utf8_unicode_ci DEFAULT NULL,
  `active` tinyint(1) DEFAULT '1',
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`),
  KEY `categories_parent_id_index` (`parent_id`),
  KEY `categories_lft_index` (`lft`),
  KEY `categories_rgt_index` (`rgt`),
  KEY `url_key` (`url_key`),
  KEY `name` (`name`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci AUTO_INCREMENT=825 ;

--
-- Dumping data for table `categories`
--
INSERT INTO `categories` (`id`, `parent_id`, `lft`, `rgt`, `depth`, `name`, `active`, `url_key`, `created_at`, `updated_at`) VALUES
(1, NULL, 1, 968, 0, 'Root', 1, NULL, '2016-01-23 13:19:51', '2016-01-23 13:19:51'),
(487, 1, 446, 453, 1, 'Mobile & Communication', 1, 'mobile-communication', '2016-01-23 13:19:51', '2016-01-23 13:19:51'),
(488, 487, 447, 448, 2, 'Mobile Phones', 1, 'mobile-phones', '2016-01-25 06:27:37', '2016-01-25 06:27:37');


--
-- Table structure for table `category_product`
--
CREATE TABLE IF NOT EXISTS `category_product` (
  `product_id` int(10) unsigned NOT NULL,
  `category_id` int(10) unsigned NOT NULL,
  PRIMARY KEY (`product_id`,`category_id`),
  KEY `product_id` (`product_id`),
  KEY `category_id` (`category_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

--
-- Dumping data for table `category_product`
--
INSERT INTO `category_product` (`product_id`, `category_id`) VALUES
(26195, 1),
(26195, 487),
(26195, 488),
(26425, 1),
(26425, 487),
(26425, 488),
(26632, 1),
(26632, 487),
(26632, 488),
(28212, 1),
(28212, 487),
(28212, 488),
(35111, 1),
(35111, 487),
(35111, 488),
(37611, 1),
(37611, 487),
(37611, 488),
(60017, 1),
(60017, 487),
(60017, 488);

コメントへの対応

5.5にダウングレードすることは、それを修正できない場合の私の最後のオプションです。クエリを書き直したいのですが、クエリを変更してみましたが、うまくいきませんでした。私が書くクエリに何か問題があると思います。

上記のテストデータの場合、クエリは(カテゴリID:488)の製品を返す必要があります。クエリは価格比較サイトに使用されます。ユーザーがWebサイトのカテゴリー(「携帯電話」-データベースのカテゴリーID-「カテゴリー」テーブルは488)リンクをクリックすると、Webサイトはすべての製品(「matchkey」列でグループ化)をリストし、その製品グループ。

WHERE categories.id = 488またはcategories.name = 'Mobile Phones'を追加しても、同じ結果が得られます。問題は、サブクエリにORDER BY pp.price_selling ASCと書いても、クエリ結果に最低価格が表示されないことです。

デモ:

上記の両方のデモは同じクエリを使用していますが、結果は異なります。

3
Bobodo
SELECT a, b ...
    GROUP BY a

トラブルを求めています。 5.5では「ラッキー」、5.6では「アンラッキー」でした。

問題は、anybの値が「グループ」ごとに表示できることです。確かに、特定のaが特定のbに一意にマップされる場合があります(たとえば、正規化テーブル内)。しかし、ここではそうではありません。問題があることを確認するには:

SET sql_mode = 'ONLY_FULL_GROUP_BY';

sELECTを実行する前に。

参照: http://dev.mysql.com/doc/refman/5.6/en/sql-mode.html#sqlmode_only_full_group_by

本当にInnoDBに切り替えるべきです。

5.7に到達すると、その設定がデフォルトでオンになります。

groupwise maxの効率的な手法

2
Rick James