🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
MySQL

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

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

SQL

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

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

Eloquent

Eloquentとは、PHPフレームワークのLaravelに最初から含まれているORM(Object-relational mapping:オブジェクト関係マッピング)です。

Q&A

解決済

3回答

2500閲覧

SQLで質問テーブルに紐付く回答結果テーブルの正解確率を抽出したい

wonda

総合スコア69

MySQL

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

Laravel

LaravelとはTaylor Otwellによって開発された、オープンソースなPHPフレームワークです。Laravelはシンプルで表現的なシンタックスを持ち合わせており、ウェブアプリケーション開発の手助けをしてくれます。

SQL

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

データベース

データベースとは、データの集合体を指します。また、そのデータの集合体の共用を可能にするシステムの意味を含めます

Eloquent

Eloquentとは、PHPフレームワークのLaravelに最初から含まれているORM(Object-relational mapping:オブジェクト関係マッピング)です。

0グッド

0クリップ

投稿2019/10/17 07:22

表題の通りなのですが、Laravel6.3にて開発中のサイトのSQLの組み立てとクエリビルダを構築するのに詰まってしまい、手が止まってしまいました。
抽出したい条件は、質問に対する回答結果を格納しており、回答が10件以上付いている質問の正解率(正解数/回答数)を抽出しし、最終的に正解率順にソートしたいと考えています。


簡易ですがテーブル状況は以下です。

・質問テーブル (questions)
id int

・回答結果テーブル (results)
id int
question_id int (外部キー参照制約)
result tinyint (0か1)


回答が10件以上付いている質問を抽出するEloquentクエリビルダは下記の通りです。

php

1Question::has('results', '>=', 10)->get();

これで以下のSQLが生成されます。

sql

1select * 2from `questions` 3where ( 4 select count(*) 5 from `results` 6 where `questions`.`id` = `results`.`question_id` 7) >= 10 8and `questions`.`deleted_at` is null

この絞り込まれた質問毎の正解率(正解数/回答数)を求める方法に詰まっております。
まずSQLで考えると、以下のようなサブクエリになるような気がするのですが、どうも構築がうまくいきません。

sql

1SELECT 2*,( 3(select count(*) from `results` where `questions`.`id` = `results`.`question_id` AND `results`.`result` = 1) AS CORRECT 4/ 5(select count(*) from `results` where `questions`.`id` = `results`.`question_id`) AS TOTAL 6) AS ACCURACY_RATE 7from questions;

クエリビルダのメソッドのみで構築出来るのか、もしくはselectRaw()DB::raw())を使う必要があるのか。
最終的には抽出した正解率をascもしくはdescでソートしたいとも考えています。
お詳しい方がおられましたら、御教示頂けると幸いでございます。
どうぞ宜しくお願い致します。

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

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

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

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

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

guest

回答3

0

ベストアンサー

正解は result = 1 なんですよね?

であれば、
全回答数=results の件数(count(*))
正解数=sum(result)
で、一回の SQL で得られませんか?

つまりこんな SQL です。

SQL

1SELECT 2 questions.id, 3 count(results.id) as total_answers_count, 4 sum(results.result) as correct_answers_count, 5 sum(results.result) * 100 / count(results.id) as correct_percent 6FROM 7 questions 8 INNER JOIN results ON (results.question_id = questions.id) 9WHERE 10 questions.id = {質問ID} 11GROUP BY 12 questions.id

これをクエリビルダで表現できれば行けそうな気はします。
ソートもできるでしょうね。

余談:
回答数が10件以上ある、という条件があるなら、

SQL

1SELECT 2 questions.id, 3 count(results.id) as total_answers_count, 4 sum(results.result) as correct_answers_count, 5 sum(results.result) * 100 / count(results.id) as correct_percent 6FROM 7 questions 8 INNER JOIN results ON (results.question_id = questions.id) 9GROUP BY 10 questions.id 11HAVING 12 count(result.id) >= 10

で、「10件以上回答のある質問全ての、回答数と正答数と正答率」が一回で得られるかと。

投稿2019/10/17 07:33

編集2019/10/17 07:41
tacsheaven

総合スコア13703

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

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

wonda

2019/10/17 08:28 編集

tacsheavenさん、ありがとうございます! MySQL集計関数を駆使する事で表現できるんですね。。 頂いたSQLのresultテーブルをresultsにして、削除フラグ考慮やlimit句などを入れればほぼ目的のデータが取れる事が判りました! 後はこれをEloquent経由のクエリビルダで表現出来れば良いんですが、これもまた難解で。。 Question::has('results', '>=', 10) ->withCount([ 'results AS results_cnt', 'results AS correct_cnt' => function (Builder $query) { $query->where('result', true); }, ]) ->selectRaw('`results`.`correct_cnt` * 100 / `results`.`results_cnt` as correct_percent') としてみるものの、なんかJOIN出来ていない気がしています。 すみませんが、方法を調べつつもう少しだけ回答を募りたいと思います。 ありがとうございました。
tacsheaven

2019/10/17 08:47

Lalabel だとこんな感じですかね? DB::Table('questions') ->join('results', 'results.question_id', '=', 'questions.id') ->select('questions.id') ->selectRaw('count(results.id) as results_cnt') ->selectRaw('sum(results.result) as correct_cnt') ->selectRaw('sum(results.result) * 100 / count(results.id) as correct_percent') ->groupBy('questions.id') ->having('count(results.id)', '>=', 10) ->orderBy('correct_percent', 'desc') ->get()
tacsheaven

2019/10/17 08:48

例えば正答率が同点の場合、どちらを上位にするか(回答数の多い方が優先かな?)とか、もう少し考えることはあるかと思いますが、基本的にはこんな感じで行けると思います
wonda

2019/10/17 13:00

ありがとうございます!! Eloquent経由ではないので後はこれに論理削除カラム考慮などが必要になりますが、DBファサードからのクエリビルダだとこれで問題無さそうです。 Eloquent経由の例を挙げて頂いた方がおられ、tacsheaven様から御教示頂いたSQLのお陰で解決となりました! お忙しいところ、本当にありがとうございました。
guest

0

tacsheaven さんのSQLをクエリビルダで書いてみた。

動作確認まではしていません。

$query = Question::query() ->selectRaw('questions.id, count(results.id) as total_answers_count, sum(results.result) as correct_answers_count, sum(results.result) * 100 / count(results.id) as correct_percent') ->join('results', 'results.question_id', '=', 'questions.id') ->groupBy('questions.id') ->havingRaw('count(result.id) >= 10') ->toSql(); dd($query);

投稿2019/10/17 08:36

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

wonda

2019/10/17 13:16 編集

ありがとうございます! Eloquentを利用する場合、リレーションクラスで簡単に済ます方法ばかり探ってしまっていましたが、`query()`でビルダインスタンスを生成して、後は普通にjoinすれば良かったのですね。 最終的に以下の形で解決となりました。 $items = Question::query() ->selectRaw('questions.*, count(results.id) as total_answers_count, sum(results.result) as correct_answers_count, sum(results.result) * 100 / count(results.id) as correct_percent') ->leftJoin('results', 'questions.id', '=', 'results.question_id') ->groupBy('questions.id') ->havingRaw('count(results.id) >= 10') ->orderBy('correct_percent', 'ASC') ->limit(3) ->get(); dd($items); 高評価とさせて頂きました! 本当にありがとうございました。
guest

0

ビルダでどのようにするかは分かりませんが、インラインビューでカウントして結合した方が高速じゃないかと思うので、参考までに。

SQL

1select questions.* 2from questions inner join ( 3 select question_id, count(*) as result_count from results group by question_id having count(*)>=10 4 ) results 5 on questions.id = results.question_id 6where questions.deleted_at is null 7order by results.result_count desc

投稿2019/10/17 08:48

sazi

総合スコア25327

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

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

wonda

2019/10/17 13:12

参考になります。 まだ試験データが足りず、速度検証まではなかなか難しいですが、実稼働後にクエリチューニングする必要が出てきた場合に検討してみます! ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問