books_book
title | price |
---|---|
フェルマーの最終定理 | 781 |
暗号解読(上) | 590 |
暗号解読(下) | 630 |
Python プロフェッショナルプログラミング | 2800 |
エキスパートPythonプログラミング | 3600 |
スタートアップ! | 1600 |
こんなテーブルがあったとして、SQLで価格毎の冊数をカウントする場合、Case式を使って以下のように書けます。
SELECT CASE WHEN price < 1000 THEN '1000円未満' WHEN price >= 1000 AND price < 2000 THEN '1000円以上2000円未満' WHEN price >= 2000 AND price < 3000 THEN '2000円以上3000円未満' WHEN price >= 3000 THEN '3000円以上' ELSE NULL END AS price_class, COUNT(*) AS cnt FROM books_book GROUP BY CASE WHEN price < 1000 THEN '1000円未満' WHEN price >= 1000 AND price < 2000 THEN '1000円以上2000円未満' WHEN price >= 2000 AND price < 3000 THEN '2000円以上3000円未満' WHEN price >= 3000 THEN '3000円以上' ELSE NULL END;
結果
+----------------------------+-----+ | price_class | cnt | +----------------------------+-----+ | 1000円未満 | 3 | | 1000円以上2000円未満 | 1 | | 2000円以上3000円未満 | 1 | | 3000円以上 | 1 | +----------------------------+-----+
DjangoのQuerySetを使ってSQLを発行する場合、自由にsqlを書く方法としてrawがありますが
result = Book.objects.raw(""" SELECT CASE WHEN price < 1000 THEN '1000円未満' WHEN price >= 1000 AND price < 2000 THEN '1000円以上2000円未満' WHEN price >= 2000 AND price < 3000 THEN '2000円以上3000円未満' WHEN price >= 3000 THEN '3000円以上' ELSE NULL END AS price_class, COUNT(*) AS cnt FROM books_book GROUP BY CASE WHEN price < 1000 THEN '1000円未満' WHEN price >= 1000 AND price < 2000 THEN '1000円以上2000円未満' WHEN price >= 2000 AND price < 3000 THEN '2000円以上3000円未満' WHEN price >= 3000 THEN '3000円以上' ELSE NULL END; """)[0]
これはエラーになります
InvalidQuery: Raw query must include the primary key
rawを使う場合は必ずprimary keyを取得しないといけないんですね。
raw以外に素の SQL クエリを直接実行するにはconnection.cursor()を使う手もあります
from django.db import connection cursor = connection.cursor() cursor.execute(""" SELECT CASE WHEN price < 1000 THEN '1000円未満' WHEN price >= 1000 AND price < 2000 THEN '1000円以上2000円未満' WHEN price >= 2000 AND price < 3000 THEN '2000円以上3000円未満' WHEN price >= 3000 THEN '3000円以上' ELSE NULL END AS price_class, COUNT(*) AS cnt FROM books_book GROUP BY CASE WHEN price < 1000 THEN '1000円未満' WHEN price >= 1000 AND price < 2000 THEN '1000円以上2000円未満' WHEN price >= 2000 AND price < 3000 THEN '2000円以上3000円未満' WHEN price >= 3000 THEN '3000円以上' ELSE NULL END; """) rows = cursor.fetchall()
これなら値を取得できました。
まあ、無理して一発で引く必要がなければ、これでも。
Book.objects.filter(price__lt=1000).count() Book.objects.filter(price__gte=1000, price__lt=2000).count() Book.objects.filter(price__gte=2000, price__lt=3000).count() Book.objects.filter(price__gte=3000).count()