ORM のクエリ最適化と SQL チューニング

Django × SQL

日付:2025年3月11日

Django ORM のクエリ最適化と SQL チューニングを解説。select_related, prefetch_related, N+1 問題の回避方法を紹介。

目 次

1. Django ORM のクエリ最適化とは?

Django の ORM は便利ですが、適切に使わないと大量のクエリが発生し、パフォーマンスが低下することがあります。
本記事では、ORM を使いながら SQL を最適化する方法を紹介します。

Django では、リレーションを持つデータを取得するときに N+1 問題 が発生することがあります。

例:N+1 問題の発生

PYTHON
users = User.objects.all() for user in users: print(user.profile.bio) # Profile を取得するたびに SQL クエリが発行される

上記コードでは、User を取得した後、Profile にアクセスするたびに個別の SQL クエリが発生します。

PYTHON
users = User.objects.select_related("profile").all()

select_relatedJOIN を使って関連データを一度に取得するため、クエリ回数が削減されます。

PYTHON
users = User.objects.prefetch_related("profile").all()

prefetch_related は複数のクエリを発行しますが、事前にキャッシュして関連データをまとめて取得できます。

3. only()defer() で不要なデータを取得しない

only() を使うと、取得するフィールドを限定できます。

PYTHON
users = User.objects.only("id", "name")

逆に defer() を使うと、不要なフィールドを除外できます。

PYTHON
users = User.objects.defer("bio")

4. クエリの実行時間を計測する

Django の QuerySet には .explain() メソッドがあり、実行計画を確認できます。

PYTHON
queryset = User.objects.filter(name="Taro") print(queryset.explain())

PostgreSQL の場合、ANALYZE を使うと詳細な情報を確認できます。

PYTHON
queryset = User.objects.filter(name="Taro") print(queryset.explain(analyze=True))

5. EXPLAIN を使った SQL の分析

Django ORM で発行される SQL のパフォーマンスを分析するには EXPLAIN を使います。

SQL
EXPLAIN ANALYZE SELECT * FROM users WHERE name = 'Taro';

6. インデックスを活用する

頻繁に検索するカラムには db_index=True を設定することで検索を高速化できます。

PYTHON
class User(models.Model): name = models.CharField(max_length=100, db_index=True)

また、複数のカラムに対する検索を最適化するには、Meta.indexes を活用できます。

PYTHON
from django.db.models import Index class User(models.Model): name = models.CharField(max_length=100) email = models.EmailField(unique=True) class Meta: indexes = [ Index(fields=["name", "email"]), ]

7. SQLite と PostgreSQL のパフォーマンスチューニングの違い

最適化手法SQLitePostgreSQL
select_related / prefetch_relatedありあり
EXPLAIN ANALYZE制限ありあり(詳細な情報を取得可能)
INDEX のサポートありあり(GIN, GiST など高機能インデックスあり)
クエリの並列処理なしあり(並列クエリの最適化が可能)

8. まとめ

  • select_relatedJOIN を使って関連データを最適化
  • prefetch_related は別クエリをキャッシュして関連データを取得
  • only()defer() で不要なデータの取得を減らす
  • EXPLAINANALYZE を使ってクエリの実行計画を確認
  • インデックスを適切に設定して検索パフォーマンスを向上