はじめに
元々現場ではJavaなどの言語を触っていたこともあり、SQLを高頻度で書いていました。
ですがDjangoを触り始めてORMでのクエリ発行になかなか詰まったので、備忘録としてまとめてみました。
当記事では以下の構成でまとめていきます。
1.こんなSQLを実行したい
↓
2.Djangoでの書き方(解説・補足等)
環境
動作確認は以下の環境で行いました。
- MySQL
■SELECT
・データの全件取得
SELECT A.*
FROM my_modles A
from models import MyModel
many_data = MyModel.objects.all()
補足:
-
all()
を指定することで全件取得が可能
・指定カラムの取得
SELECT A.col1, A.col2....
FROM my_modles A
from models import MyModel
many_data = MyModel.objects.values("col1","col2")
# または
many_data = MyModel.objects.values_list("col1","col2")
# または
many_data = MyModel.objects.only("col1","col2")
補足:
-
values()
→ 値を辞書型のリストで返却する
[
{'col1': 'value1_1', 'col2': 'value1_2'},
{'col1': 'value2_1', 'col2': 'value2_2'},
...
]
-
values_list()
→ 値をタプル型のリストで返却する
[
('value1_1', 'value1_2'),
('value2_1', 'value2_2'),
...
]
values_list
はflat
オプションを持っており単一カラムの取得時に便利
many_data = MyModel.objects.values_list("col1", flat=True).all()
print(many_data)
# タプルではなくリスト形式になり、データへのシンプルなアクセスが可能
# => ['value1', 'value2', 'value3', ...]
-
only()
→ 値をモデルのインスタンスで返却する
MyModel Instance: <MyModel: MyModel object (1)>, Value1: test_val, Value2: 500.00
MyModel Instance: <MyModel: MyModel object (2)>, Value1: test_val2, Value2: 200.00
■FROM
■前提
JOIN
に関してはrelationがmodel
設定されていることがとても重要になります。
Djangoでは関連先のオブジェクトを取得し、クエリ発行回数を減らすことでパフォーマンスの向上につながります。
・INNER JOIN
SELECT A.*, B.*
FROM my_modles A
INNER JOIN other_models B
ON A.pk = B.my_models_id
from models import MyModel
from models import OtherModel
many_data = MyModel.objects.select_related("other_model").all()
補足:
-
select_related
を使用することでINNER JOIN
が可能になる
○仕様
- 対一の関係性を持つテーブルを取得する
→ 多対一(ForeignField
)、一対一(OneToOneField
)
# 関連先のオブジェクトデータを取得する
many_data = MyModel.objects.select_related("other_model").all()
other_data = many_data.other_model
・LEFT JOIN
SELECT A.*, B.*
FROM my_modles A
LEFT OUTER JOIN other_models B
ON A.pk = B.my_models_id
from models import MyModel
from models import OtherModel
many_data = MyModel.objects.prefetch_related("other_model").all()
補足:
-
prefetch_related
を使用することでLEFT JOIN
が可能になる
○仕様
- 対多の関係性を持つテーブルを取得する
→ 多対多(ManyToManyField
)
※厳密には異なる箇所がありますが割愛させてください。
■WHERE
・取得件数
SELECT *
FROM my_modles A
WHERE A.hoge_col = "hoge"
from models import MyModel
# 複数件取得
many_data = MyModel.objects.filter(hoge_col="hoge")
# 1件取得
many_data = MyModel.objects.filter(hoge_col="hoge").first()
#または
many_data = MyModel.objects.get(pk=1)
補足:
-
filter
とget
の違いと特徴
get
:
- 単一のオブジェクトを返却する。
- 取得件数が0件の場合DoesNotExist
エラーが発生する
filter
:
- 複数のオブジェクトを返却する。
- 取得件数が0件でもエラーが発生しない。
- 取得オブジェクトに対し.query
を使用し、発行されるクエリを確認できる
・演算子
⚪︎AND/OR
■前提
Djangoではデフォルトの論理演算子はAND
が使用されてしまう。
論理演算子を容易に実装可能にしてくれるのが、Qオブジェクト
である
SELECT *
FROM my_modles A
WHERE
(A.hoge_col1 = "hoge" and not A.hoge_col2 = "huga")
or A.hoge_col3 = "piyo"
from django.db.models import Q
from models import MyModel
many_data = MyModel.objects.filter(
( Q(hoge_col1="hoge") & ~Q(hoge_col2="huga") )
| Q(hoge_col3="piyo")
)
補足:
-
AND
は&
記号を用いる -
OR
は|(パイプ)
記号を用いる -
NOT
の場合 は~
記号をQ
の前に用いる
⚪︎比較
SELECT *
FROM my_modles A
WHERE
A.number1 > hoge
or A.number2 >= 3
or A.number3 < 3
or A.number4 <= 3
from django.db.models import Q
from models import MyModel
many_data = MyModel.objects.filter(
Q(number1__gt=3)
| Q(number2__gte=3)
| Q(number3__lt=3)
| Q(number4__lte=3)
)
補足:
-
__
(アンダースコア2つ)が必要
⚪︎LIKE句
SELECT *
FROM my_modles A
WHERE
A.hoge_col1 LIKE '%hoge'
OR A.hoge_col2 LIKE 'hoge%'
OR A.hoge_col3 LIKE '%hoge%'
from django.db.models import Q
from models import MyModel
many_data = MyModel.objects.filter(
Q(hoge_col1__startwith="hoge")
| Q(hoge_col2__endwith="hoge")
| Q(hoge_col3__contains="hoge")
)
補足:
- 前方一致 →
startwith
- 後方一致 →
endwith
- 部分一致 →
contains
- それぞれの頭に
i
を付与することで大文字、小文字の区別を行わなくなる(例:istartwith
)
⚪︎IN句
SELECT *
FROM my_modles A
WHERE
A.hoge_col in (1,3)
from models import MyModel
list = [1,3]
many_data = MyModel.objects.filter(hoge_col__in=list)
補足:
-
__
(アンダースコア2つ)が必要 - 検索したい値を
list
またはtuple
で渡す
⚪︎BETWEEN句
SELECT *
FROM my_modles A
WHERE
A.hoge_col BETWEEN 1 and 10
from models import MyModel
list = [1,3]
many_data = MyModel.objects.filter(A.hoge_col__range=(1, 10))
補足:
-
__
(アンダースコア2つ)が必要
■GROUP BY
SELECT A.id, A.name, SUM(A.hoge_num) as sum_data
FROM my_modles A
GROUP BY A.id, A.name
from django.db.models import Sum
from models import MyModel
many_data = MyModel.objects.annotate(sum_data=Sum("hoge_col")).only("id", "name", "sum_data")
補足:
-
annotate
を用いて集計を行うカラムを用意 - 内部的に
GROUP_BY
してくれるが、必要に応じてonly()
メソッドを利用して任意のGROUP BY
が可能になる
■HAVING
SELECT A.id, A.name, SUM(A.hoge_col) as sum_data
FROM my_modles A
WHERE name LIKE '%hoge%'
GROUP BY A.id, A.name
HAVING sum_data > 5
from django.db.models import Sum
from models import MyModel
many_data = MyModel.objects
.filter(name__contains="hoge")
.annotate(sum_data=Sum("hoge_col"))
.filter(sum_data__gt=5)
補足:
-
annotate
を用いたのちにfilter
でHAVING
の指定が可能 -
WHERE
、HAVING
を一つのfilter
で指定できない
■ORDER BY
SELECT A.*
FROM my_modles A
ORDER BY A.hoge_col asc
--ORDER BY A.hoge_col desc
from models import MyModel
asc_data = MyModel.objects.all().order_by("hoge_col")
desc_data = MyModel.objects.all().order_by("-hoge_col")
補足:
-
desc(降順)
の場合から無名の前に-
をつける
■その他関数
・COUNT
SELECT A.col1, A.col2, Count(A.col3) as count_col
FROM my_modles A
from django.db.models import Count
from models import MyModel
many_data = MyModel.objects.annotate(count_col=Count("col3"))
・SUM
SELECT A.col1, A.col2, Sum(A.col3) as sum_col
FROM my_modles A
from django.db.models import Sum
from models import MyModel
many_data = MyModel.objects.annotate(sum_col=Sum("col3"))
CASE/WHEN
SELECT
A.col1
, A.col2
, CASE col3
WHEN 1 THEN "one"
WHEN 2 THEN "two"
WHEN 3 THEN "three"
ELSE "other"
END as case_col
FROM my_modles A
from django.db.models import Case, When
from models import MyModel
many_data = MyModel.objects.annotate(
case_col=Case(
When(col3=1, then=Value("one"))
,When(col3=2, then=Value("two"))
,When(col3=3, then=Value("three"))
,default=Value("other")
,output_field=models.CharField()
)
補足:
-
Case
内にWhen
を引数として渡し使用する -
default
はELSE
の役割を担う -
output_field
は戻り値の型を明示的に示す
■INSERT
更新中・・・
■UPDATE
更新中・・・