はじめに
この記事は2023年度の振り返りです。
こちらの続きとなります。
問題を解いてる時の実話を元に、(実験で)対話形式で書いてみました
問題
こんなテーブルがあったとする
(テーブル構成に突っ込みどころは多数あると思いますが、割愛して見逃してください)
irb(main):003> Author.all
Author Load (0.4ms) SELECT `authors`.* FROM `authors`
+----+-----------+-------------------------+-------------------------+
| id | name | created_at | updated_at |
+----+-----------+-------------------------+-------------------------+
| 1 | 岸本 斉史 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 2 | 村田 雄介 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 3 | 冨樫 義博 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 4 | 鳥山 明 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
+----+-----------+-------------------------+-------------------------+
irb(main):005> Write.all
Write Load (0.5ms) SELECT `writes`.* FROM `writes`
+----+---------+---------+-------------------------+-------------------------+
| id | book_id | user_id | created_at | updated_at |
+----+---------+---------+-------------------------+-------------------------+
| 1 | 1 | 1 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 2 | 2 | 1 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 3 | 3 | 2 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 4 | 4 | 2 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 5 | 5 | 3 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 6 | 6 | 3 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 7 | 7 | 4 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 8 | 8 | 4 | 2023-12-22 14:32:30 UTC | 2023-12-22 14:32:30 UTC |
+----+---------+---------+-------------------------+-------------------------+
irb(main):004> Comic.all
Comic Load (0.5ms) SELECT `comics`.* FROM `comics`
+----+------------------+-------------------------+-------------------------+
| id | name | created_at | updated_at |
+----+------------------+-------------------------+-------------------------+
| 1 | NARUTO -ナルト- | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 2 | BORUTO -ボルト- | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 3 | アイシールド21 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 4 | ワンパンマン | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 5 | 幽☆遊☆白書 | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 6 | HUNTER×HUNTER | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 7 | Dr.スランプ | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
| 8 | ドラゴンボール | 2023-12-22 14:32:29 UTC | 2023-12-22 14:32:29 UTC |
+----+------------------+-------------------------+-------------------------+
Author(作者テーブル)で、「冨樫 義博」が書いている漫画を取得するSQLをactive recordで作成してください
勉強会内の小話
初学者達:たぶんこんな感じじゃないですか?
author = Author.find_by(name: '冨樫 義博')
write_ids = Write.where(user_id: author.id).pluck(:book_id)
p Comic.where(id: write_ids)
講師:戻ってくる解答としては正しいけど、これ3回クエリ走るんじゃない?
SELECT `authors`.* FROM `authors` WHERE `authors`.`name` = '冨樫 義博' LIMIT 1
SELECT `writes`.`book_id` FROM `writes` WHERE `writes`.`user_id` = 3
SELECT `comics`.* FROM `comics` WHERE `comics`.`id` IN (5, 6)
初学者達:ですね
講師:解き方は色々あるだろうけど、まずはサブクエリを使ってクエリ減らせるよ?
初学者達:サブクエリってなんですか?
講師:よ、よし。まずはここで勉強しよう
初学者達:でも、これってSQLの話であって、active recordじゃ作れませんよね
講師:普通にできる・・・よ?
講師:さっき解答したやつを少し変えるだけ
author = Author.where(name: '冨樫 義博')
write = Write.where(user_id: author).select(:book_id)
p Comic.where(id: write)
SELECT `comics`.*
FROM `comics`
WHERE `comics`.`id` IN (
SELECT `writes`.`book_id`
FROM `writes`
WHERE `writes`.`user_id` IN (
SELECT `authors`.`id`
FROM `authors`
WHERE `authors`.`name` = '冨樫 義博'
)
)
初学者達:おーー。クエリが一つになった!
初学者達:でも、なんでこっちにはselect書いてるんですか?
author = Author.where(name: '冨樫 義博')
write = Write.where(user_id: author).select(:book_id) # ←ココ
p Comic.where(id: write)
講師:書かない場合、デフォルトでIDが利用される。試しにやってみよう
author = Author.where(name: '冨樫 義博')
write = Write.where(user_id: author)
p Comic.where(id: write)
SELECT `comics`.*
FROM `comics`
WHERE `comics`.`id` IN (
SELECT `writes`.`id` -- ← ココが変わってる
FROM `writes`
WHERE `writes`.`user_id` IN (
SELECT `authors`.`id`
FROM `authors`
WHERE `authors`.`name` = '冨樫 義博'
)
)
講師:結果、意味の異なる検索結果になるんで、この場合は必要ですね。まぁ、明示的にきちんとselect(:id)と書いてやってた方が、メンバーに優しいとは思うよ。
初学者達:なるほど!
さいごに
初の対話形式の記事を書いてみました。
少し短いですが、今回はここまでとします。
この勉強会の話は色々あるので、可能な範囲短く区切ってシリーズにしてみようと思います。