前提・実現したいこと
データベース MySQL 5.7.33
開発環境 AWS cloud9 (t2.micro)
** 開発言語 PHP7.0
** フレームワーク Laravel 5.1.6
LaravelでWebアプリケーションの開発を行っているのですが、
クエリビルダの問題というよりもデータ自体に問題がありそうだと考えたため、
SQL言語ベースで質問をさせていただきます。内容に不備があればお知らせいただければ幸いです。
実現したい内容
「ある商品の売上個数が最大だった日を支店ごとに割り出す。」
参考にしたサイト
Qitta - MySQL でグループごとの最大値を持つレコードを取得する
→ このうち、「パターン3 WHERE NOT EXISTS」に沿ってクエリビルダを組んでいきました。
SQL Fiddle
→ Laravelで記述したクエリを toSql()
メソッドでSQL文に直し、上記サイト上で検証を行っています。
テーブルの内容
SQL
1CREATE TABLE `projects` ( 2 `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 3 `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 4 `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 5 `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 6 PRIMARY KEY (`id`) 7) ENGINE=InnoDB DEFAULT CHARSET=utf8; 8 9INSERT INTO `projects` VALUES 10(998,'鉛筆ショップ','2021-07-01 00:00:00','2021-07-01 00:00:00'), 11(999,'飴ちゃん屋','2021-07-01 00:00:00','2021-07-01 00:00:00'); 12 13CREATE TABLE `shops` ( 14 `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 15 `project_id` int(10) unsigned NOT NULL, 16 `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 17 `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 18 `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 19 PRIMARY KEY (`id`), 20 KEY `shops_project_id_index` (`project_id`) 21) ENGINE=InnoDB DEFAULT CHARSET=utf8; 22 23INSERT INTO `shops` VALUES 24(1,999,'大阪本店','2021-07-01 00:00:00','2021-07-01 00:00:00'), 25(2,999,'東京支店','2021-07-01 00:00:00','2021-07-01 00:00:00'); 26 27CREATE TABLE `earnings` ( 28 `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 29 `project_id` int(11) NOT NULL, 30 `shop_id` int(11) NOT NULL, 31 `item_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 32 `item_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 33 `earned_date` date NOT NULL, 34 `comment` text COLLATE utf8_unicode_ci NOT NULL, 35 `earning` int(10) unsigned NOT NULL, 36 `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 37 `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 38 PRIMARY KEY (`id`), 39 KEY `earnings_project_id_index` (`project_id`), 40 KEY `earnings_shop_id_index` (`shop_id`) 41) ENGINE=InnoDB DEFAULT CHARSET=utf8; 42 43INSERT INTO `earnings` VALUES 44(1,999,1,'1','リンゴ味','2021-06-15','まあまあ売れた。',10,'2021-07-01 00:00:00','2021-07-01 00:00:00'), 45(2,999,1,'2','ブドウ味','2021-06-20','少し売れた。',5,'2021-07-01 00:00:00','2021-07-01 00:00:00'), 46(3,999,1,'1','リンゴ味','2021-06-30','かなり売れた。',20,'2021-07-01 00:00:00','2021-07-01 00:00:00');
このうち、earnings
テーブルには、ある店舗(shop_id
)のある日(earned_date
)におけるある商品(item_id
)の総売上個数(earning
)を、それひとつのみ入れていく形になります。
(テーブルの定義でunique
系の制約もなく、分かりにくくて申し訳ありません…。)
なお、item
に関するテーブルもあるのですが、必要な情報はearnings
テーブルに織り込んでいて結合にも使用しないため、ここでは割愛します。
実行内容
SQL
1SELECT 2 `shops`.`id` AS `shop_id`, 3 `E1`.`id` AS `earning_id`, 4 `E1`.`earning` AS `max_earning`, 5 `E1`.`earned_date` AS `earned_date`, 6 `E1`.`comment` AS `comment` 7FROM 8 `shops` 9LEFT JOIN 10 `earnings` AS `E1` 11ON 12 `E1`.`shop_id` = `shops`.`id` 13AND 14 `E1`.`item_id` = 2 # ** POINT ** 15WHERE 16 `shops`.`project_id` = 999 17AND 18 NOT EXISTS( 19 SELECT 20 1 21 FROM 22 `earnings` AS `E2` 23 WHERE 24 E1.shop_id = E2.shop_id 25 AND 26 E1.earning < E2.earning 27 ) 28;
発生している問題
上記コードの** POINT **
行にて、値を1にした場合と2にした場合で実行結果が変わってしまいます。
<E1.item_id = 1
の場合>
<E1.item_id = 2
の場合>
shop_id=1
でかつitem_id=2
のレコードは一つ用意されているはずなのに、それがスキップされてしまいます。
試したこと
● item_id
のデータ型が varchar
になっている点に着目
[1] データ型とINSERTを修正する → 実行結果変わらず
SQL
1CREATE TABLE `earnings` ( 2 `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 3 `project_id` int(11) NOT NULL, 4 `shop_id` int(11) NOT NULL, 5 `item_id` int(11) NOT NULL, 6 # `item_id` varchar(255) COLLATE utf8_unicode_ci NOT NULL, ** ここを上記に修正 7 `item_name` varchar(255) COLLATE utf8_unicode_ci NOT NULL, 8 `earned_date` date NOT NULL, 9 `comment` text COLLATE utf8_unicode_ci NOT NULL, 10 `earning` int(10) unsigned NOT NULL, 11 `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 12 `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', 13 PRIMARY KEY (`id`), 14 KEY `earnings_project_id_index` (`project_id`), 15 KEY `earnings_shop_id_index` (`shop_id`) 16) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8; 17 18INSERT INTO `earnings` VALUES 19(1,999,1,1,'リンゴ味','2021-06-15','まあまあ売れた。',10,'2021-07-01 00:00:00','2021-07-01 00:00:00'), 20(2,999,1,2,'ブドウ味','2021-06-20','少し売れた。',5,'2021-07-01 00:00:00','2021-07-01 00:00:00'), 21(3,999,1,1,'リンゴ味','2021-06-30','かなり売れた。',20,'2021-07-01 00:00:00','2021-07-01 00:00:00'); 22#********↑ ここの''を外す
[2] WHEREクエリを文字列検索向けに修正する → 実行結果変わらず
SQL
1SELECT 2 `shops`.`id` AS `shop_id`, 3 `E1`.`id` AS `earning_id`, 4 `E1`.`earning` AS `max_earning`, 5 `E1`.`earned_date` AS `earned_date`, 6 `E1`.`comment` AS `comment` 7FROM 8 `shops` 9LEFT JOIN 10 `earnings` AS `E1` 11ON 12 `E1`.`shop_id` = `shops`.`id` 13AND 14 `E1`.`item_id` LIKE '2' # ** ここを修正 ** 15WHERE 16 `shops`.`project_id` = 999 17AND 18 NOT EXISTS( 19 SELECT 20 1 21 FROM 22 `earnings` AS `E2` 23 WHERE 24 E1.shop_id = E2.shop_id 25 AND 26 E1.earning < E2.earning 27 ) 28;
● いじった中での発見…
[3] 15,16行目の project_id
による絞込み制約を消すと、中身は(null)
だが一応shop_id=1
の列が立つ
SQL
1SELECT 2 `shops`.`id` AS `shop_id`, 3 `E1`.`id` AS `earning_id`, 4 `E1`.`earning` AS `max_earning`, 5 `E1`.`earned_date` AS `earned_date`, 6 `E1`.`comment` AS `comment` 7FROM 8 `shops` 9LEFT JOIN 10 `earnings` AS `E1` 11ON 12 `E1`.`shop_id` = `shops`.`id` 13AND 14 `E1`.`item_id` = 2 # ** POINT ** 15AND 16 NOT EXISTS( 17 SELECT 18 1 19 FROM 20 `earnings` AS `E2` 21 WHERE 22 E1.shop_id = E2.shop_id 23 AND 24 E1.earning < E2.earning 25 ) 26;
追記
必要であればLaravelのクエリビルダを掲載することも可能です。
ただし、このページとは別の質問としてお伺いする可能性もあります。予めご了承ください。
↑ この質問ページ内でご回答を頂く中で、Laravelでの問題も解消できると確信を持てました。本当にありがとうございました。
また、念のため申し上げますと、ここでは自分の手元にあるコードを別の例に書き換えて記載しております。
データの内容などは架空のものになりますので、安心してご検証いただければ幸いです。
回答2件
あなたの回答
tips
プレビュー