質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

Q&A

解決済

2回答

2663閲覧

【解決】データのMAX値を持つレコードをGROUP BY を使わずに絞込み、別のテーブルに結合させると、思うような結果が得られない

hika-rakuyo

総合スコア15

MySQL

MySQL(マイエスキューエル)は、TCX DataKonsultAB社などが開発するRDBMS(リレーショナルデータベースの管理システム)です。世界で最も人気の高いシステムで、オープンソースで開発されています。MySQLデータベースサーバは、高速性と信頼性があり、Linux、UNIX、Windowsなどの複数のプラットフォームで動作することができます。

SQL

SQL(Structured Query Language)は、リレーショナルデータベース管理システム (RDBMS)のデータベース言語です。大きく分けて、データ定義言語(DDL)、データ操作言語(DML)、データ制御言語(DCL)の3つで構成されており、プログラム上でSQL文を生成して、RDBMSに命令を出し、RDBに必要なデータを格納できます。また、格納したデータを引き出すことも可能です。

データ構造

データ構造とは、データの集まりをコンピュータの中で効果的に扱うために、一定の形式に系統立てて格納する形式を指します。(配列/連想配列/木構造など)

AWS(Amazon Web Services)

Amazon Web Services (AWS)は、仮想空間を機軸とした、クラスター状のコンピュータ・ネットワーク・データベース・ストーレッジ・サポートツールをAWSというインフラから提供する商用サービスです。

1グッド

0クリップ

投稿2021/07/17 09:44

編集2021/07/19 00:30

前提・実現したいこと

データベース 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の場合>
実行結果1
<E1.item_id = 2の場合>
実行結果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;

→ 実行結果
実行結果3

追記

必要であればLaravelのクエリビルダを掲載することも可能です。
ただし、このページとは別の質問としてお伺いする可能性もあります。予めご了承ください。

↑ この質問ページ内でご回答を頂く中で、Laravelでの問題も解消できると確信を持てました。本当にありがとうございました。

また、念のため申し上げますと、ここでは自分の手元にあるコードを別の例に書き換えて記載しております。
データの内容などは架空のものになりますので、安心してご検証いただければ幸いです。

neonemo👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

neonemo

2021/07/17 12:44

要件を確認したいのですが、 店舗(shops)毎の最大の利益?(earning)の品目を取り出したい って事ですか? で、それを具体化する為に ①2つのテーブル「shops」を「earnings」を「shop_idが等しい」という条件で連結し ②earningsの最大値maxを求めたい という事でしょうか? ちなみに、問題点としている【`E1`.`item_id` = 2 # ** POINT **】ですが 連結条件を【item_id=2】とした為に、【item_id=1】のレコードが除外されているだけに見えます。 where句を外せばそもそも候補に入っていなかった確認できると思います。
hika-rakuyo

2021/07/17 16:44 編集

初めまして、ご質問ありがとうございます。 貴殿の仰っている要件とは少し異なります。 1商品に対して、最大売上個数を出した「日にち」を店舗ごとに割り出したいと思っています。(データの格納ルールを記載しておらず、混乱を招いてしまいました。申し訳ありません。記事に要件とルールを追加しておきます。) 簡単で恐縮ですが、回答とさせていただきます。
guest

回答2

0

ベストアンサー

EXISTSのサブクエリにAND E1.item_id = E2.item_idの条件が抜けているのが問題のように見えます。

投稿2021/07/18 00:30

neko_the_shadow

総合スコア2230

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hika-rakuyo

2021/07/19 00:07

初めまして、ご回答いただきありがとうございます。 頂いたアドバイスのように条件を足したところ、確かにうまくいきました。 なるほど、テーブルE2内ではあくまで同一shop内のearning最大値(item_idを問わない)を探していたに過ぎないのですね。E1側でitem_idの条件を書いた時点で何故か「これで充分」と思っていた自分がいました…。サブクエリにいるのはあくまで別のテーブルですものね。 見過ごしていた問題に気付かせていただいたので、こちらをBAとさせていただきます。
guest

0

これでご期待の結果になりませんでしょうか?

SQL

1 2SElECT 3 `E1`.`shop_id` 4 , `E1`.`id` AS `earning_id` 5 , `E1`.`earning` AS `max_earning` 6 , `E1`.`earned_date` AS `earned_date` 7 , `E1`.`comment` AS `comment` 8FROM `earnings` AS `E1` 9INNER JOIN ( 10 # shop毎、item毎の最大売上個数を求めるサブクエリ 11 SELECT 12 `shop_id` 13 , `item_id` 14 , MAX(`earning`) AS `MAXearning` 15 FROM `earnings` 16 GROUP BY 17 `shop_id` 18 , `item_id` 19 ) AS `E2` 20ON `E1`.`shop_id` = `E2`.`shop_id` 21AND `E1`.`item_id` = `E2`.`item_id` 22AND `E1`.`earning` = `E2`.`MAXearning` 23WHERE 24 `E1`.`item_id` = ? #必要に応じて値を入れる 25

投稿2021/07/17 13:38

odataiki

総合スコア938

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hika-rakuyo

2021/07/18 23:42

初めまして、ご回答ありがとうございます。 shop・itemごとにearningのMAX値だけ(MAXearning)を抽出したサブクエリを用意して、元のテーブルのearningのうちサブクエリで生成したテーブルのMAX値と一致するものを持ってくる、というような意味合いですね。これを採用することもできそうです。
hika-rakuyo

2021/07/18 23:58

ただ、ベタ打ちで試して気になったのは、今回あえて何もレコードを入れていなかった shop_id=2 の場合が入ってこない部分ですね…。earningテーブルに該当するレコードが入っていないのだから、集計にかけられなくて当たり前です。 「売上が一切発生しなかった商品についても、earnings = 0 を必ず入力する」というルールを徹底できればいい話ですよね…。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問