select_related の sql を見てみる

2022年5月3日

select_related の sql を見てみる

ORMを雰囲気で使っているので少しずつ理解を深めていきたいと思っている今日この頃です。
select_relatedを使うとデータベースへのアクセス回数を減らせて負荷を軽減できるみたいです。

参考:
  select_related()の仕様を理解する
  Django ORM の SQL を出力する方法まとめ  

モデル

モデルは下の通り。


from django.db import models
class Writer(models.Model):
    name = models.CharField(max_length=128)
class Article(models.Model):
    writer = models.ForeignKey(Writer, on_delete=models.CASCADE , related_name='articles')
    title = models.CharField(max_length=512)
    contents = models.TextField()

データベース

データは下の通り。


MariaDB [djangodatabase]> select * from myapp_writer;
+----+-----------+
| id | name      |
+----+-----------+
|  1 | satoh     |
|  2 | suzuki    |
|  3 | takahashi |
+----+-----------+
MariaDB [djangodatabase]> select * from myapp_article;
+----+--------+------------------------+-----------+
| id | title  | contents               | writer_id |
+----+--------+------------------------+-----------+
|  1 | java   | java for beginner      |         1 |
|  2 | python | python for beginner    |         1 |
|  3 | R      | R for datascientist    |         2 |
|  4 | ruby   | practice ruby on rails |         3 |
+----+--------+------------------------+-----------+

プログラム

select_related の構文は下の通りです。


[クラス名].objects.select_related('[foreignKeyのフィールド]').xxx

それでは myapp_article テーブルから myapp_writer テーブルの情報を取得してみたいと思います。


python3.6 manage.py shell
>>> from myapp.models import Writer, Article
>>> data = Article.objects.select_related('writer').get(id=1)
>>> writer = data.writer
>>> writer_name = writer.name
>>> print(writer_name)
satoh
python3.6 manage.py shell
>>> from myapp.models import Writer, Article
>>> data = Article.objects.get(id=1)
>>> writer = data.writer
>>> writer_name = writer.name
>>> print(writer_name)
satoh

これらのコードは同じことをやっているようですが参考のリンクによると、前者はデータベースへのアクセスが1回で後者はデータベースへのアクセスが2回のようです。と言う事で「select_related」のクエリを見てみたいと思います。


>>> query = Article.objects.select_related('writer').query
>>> print(query)
SELECT myapp_article.id, myapp_article.writer_id, myapp_article.title, myapp_article.contents, myapp_writer.id, myapp_writer.name FROM myapp_article INNER JOIN myapp_writer ON (myapp_article.writer_id = myapp_writer.id)

きちんとJOINしているみたいですね。下記の通りに記述すると接続先のテーブルの値を一回取得する事が出来ます。


>>> writer_name = Article.objects.select_related('writer').values('writer__name').get(id=3)
>>> print(writer_name)
{'writer__name': 'suzuki'}

2022年5月3日