サブクエリを使うのって、脱衣チェスみたいなもんだよね。最初は簡単そうに見えるけど、ミスった瞬間に全部バレる。で、ミスはどこから来るの?シンタックスの理解不足だったり、SQLのロジックのクセを無視したり、ただのうっかりだったり。このレクチャーでは、よくあるミスと、その回避法について話すよ。
シンタックスのミス
サブクエリはシンタックスにめっちゃ気をつけないとダメ。カンマやカッコ、エイリアスの付け忘れで、クエリが全部壊れることも。いくつか典型的な問題を見てみよう。
カッコの付け忘れ
サブクエリではカッコが超重要。サブクエリは必ず丸カッコで囲む必要があって、1つでも足りないとシンタックスエラーになるよ。
ミス例:
SELECT student_name
FROM students
WHERE student_id IN SELECT student_id FROM enrollments);
エラー:
ERROR: syntax error at or near "SELECT"
修正例:
SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments);
コメント: INの中のクエリは必ずカッコで囲もう。SQLはそれでサブクエリだって分かるから。
エイリアスの付け忘れ
FROMの中でサブクエリを使うときは、絶対にエイリアスを付けてね。これがないとPostgreSQLは混乱しちゃう。
ミス例:
SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id)
WHERE avg_score > 80;
エラー:
ERROR: subquery in FROM must have an alias
修正例:
SELECT student_name, avg_score
FROM (SELECT student_id, AVG(score) AS avg_score FROM grades GROUP BY student_id) AS subquery
WHERE avg_score > 80;
コメント: PostgreSQLは、FROMのサブクエリ(テンポラリテーブル)に必ず名前を付ける必要があるよ。
パフォーマンスの問題
サブクエリ、特に最適化されてないやつは、DBをめっちゃ遅くする。パフォーマンスが落ちるのは、無駄な計算やインデックスがないのが原因なことが多い。
無駄な計算
SELECTの中のサブクエリは、結果の各行ごとに計算されることがあって、めっちゃ時間かかる。
例:
SELECT student_name,
(SELECT COUNT(*) FROM enrollments WHERE enrollments.student_id = students.student_id) AS course_count
FROM students;
studentsテーブルに何万行もあったら、このサブクエリは毎回全部計算し直すことになる。
最適化:
WITH course_counts AS (
SELECT student_id, COUNT(*) AS course_count
FROM enrollments
GROUP BY student_id
)
SELECT s.student_name, c.course_count
FROM students s
LEFT JOIN course_counts c ON s.student_id = c.student_id;
CTE(Common Table Expression)やJOINを使えば、毎回計算し直さなくて済むよ。このやり方はもうちょい進んだレベルでまたやるね :P
インデックスがない
WHEREの中で複雑なサブクエリを使うなら、必要なカラムにインデックスがあるか確認しよう。
例:
SELECT student_name
FROM students
WHERE student_id IN (SELECT student_id FROM enrollments WHERE course_id = 10);
enrollmentsテーブルのstudent_idカラムにインデックスがなかったら、サブクエリは全件スキャンになっちゃう。
最適化: インデックスを作ろう:
CREATE INDEX idx_enrollments_course_id ON enrollments (course_id);
インデックスの話、何回も出てくるけど、そろそろ気になってきたでしょ?でももうちょい先でやるから待ってて。インデックスはクエリの速さを上げるためのもので、クエリ自体の書き方は変えないよ。悪いクエリを良くするんじゃなくて、productionで何百万行もあるテーブルのクエリを速くするためのもの。
ロジックのミス
サブクエリのロジックミスもシンタックスミスと同じくらいよくある。NULLの扱いやフィルター、集約関数の使い方を間違えると、思った通りの結果にならない。
NULLの扱いミス
NULLは初心者を待ち受ける罠。サブクエリでINやNOT INを使うとき、NULLがあると結果に影響するよ。
ミス例:
SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments);
enrollmentsテーブルにstudent_id = NULLな行があると、このクエリは何も返さない。NOT INの条件がNULL IS NOT INみたいになるから。
修正例:
SELECT student_name
FROM students
WHERE student_id NOT IN (SELECT student_id FROM enrollments WHERE student_id IS NOT NULL);
NOT INを使うときは、必ずNULLをフィルターしよう。
フィルター条件のミス
サブクエリで複雑なフィルターを使うとき、条件を間違えると全然違う結果になることも。
ミス例:
SELECT student_name
FROM students
WHERE (SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id) > 80;
もしどれかの学生に成績がなかったら、サブクエリはNULLを返して、その学生は結果に入らない。
修正例:
SELECT student_name
FROM students
WHERE COALESCE((SELECT AVG(score) FROM grades WHERE grades.student_id = students.student_id), 0) > 80;
COALESCEを使って、NULLをデフォルト値に置き換えよう。
ミスを防ぐためのアドバイス
サブクエリでよくあるミスを避けるには、次のルールを守ろう:
カッコとエイリアスをちゃんと使う。 うまく動かないときは、カッコが全部閉じてるか、サブクエリにエイリアスが付いてるか確認しよう。
クエリの最適化。 サブクエリを減らして、JOINやWITH、インデックスを使えるところは使おう。
NULLの考慮。 サブクエリでNULLが出る可能性をいつも考えて、IS NOT NULLやCOALESCEなどを使おう。
テスト。 サブクエリは1個ずつテストして、ちゃんと期待通りの結果が返るか確認しよう。
読みやすさ。 インデントやエイリアスを使って、コードを読みやすくしよう。1ヶ月後には自分でも何書いたか忘れてるかもよ。
GO TO FULL VERSION