25. 欲しいデータ: personごとに、2つの年(year1, year2)の成績を一覧化
実行効率の良い、生々しいSQL
生SQLを書いて実行しよう
2020/8/28 25
SELECT
p.name AS name,
g1.seiseki AS grade1,
g2.seiseki AS grade2
FROM person AS p
LEFT OUTER JOIN grade AS g1 ON ((p.id = g1.person_id) AND (g1.year = 2018))
LEFT OUTER JOIN grade AS g2 ON ((p.id = g2.person_id) AND (g2.year = 2019))
name year1_grade year2_grade
===== =========== ===========
Ham 4 5
Spam 4 2
Egg None 4
26. 生SQLをrawメソッドで実行する
NG!!
文字列操作でSQLに値を埋め込むと、SQLインジェクションの原因に!
生SQLの落とし穴
2020/8/28 26
>>> year1, year2 = 2019, 2020
>>> sql = """
SELECT
p.id,
p.name,
g1.seiseki AS grade1,
g2.seiseki AS grade2
FROM person AS p
LEFT OUTER JOIN grade AS g1 ON ((p.id = g1.person_id) AND (g1.year = %s))
LEFT OUTER JOIN grade AS g2 ON ((p.id = g2.person_id) AND (g2.year = %s))
""".format(year1, year2)
>>> qs = Person.objects.raw(sql)
>>> for raw in qs:
... print(f"{p.name}, {p.grade1}, {p.grade2}")
27. プレースホルダ (%s) を使い、ドライバ側で値をエスケープ処理する
課題
動的な条件追加などは文字列操作しかない(WHERE句追加など)
パラメータのエスケープ処理
2020/8/28 27
>>> year1, year2 = 2019, 2020
>>> sql = """
SELECT
p.id,
p.name,
g1.seiseki AS grade1,
g2.seiseki AS grade2
FROM person AS p
LEFT OUTER JOIN grade AS g1 ON ((p.id = g1.person_id) AND (g1.year = %s))
LEFT OUTER JOIN grade AS g2 ON ((p.id = g2.person_id) AND (g2.year = %s))
"""
>>> qs = Person.objects.raw(sql, [year1, year2])
>>> for raw in qs:
... print(f"{p.name}, {p.grade1}, {p.grade2}")
生SQLを使う前に、ORMの使用を考え
て! ― 素の SQL 文の実行 より
31. テーブル定義 (ORMのモデル定義が不要な場合)
クエリビルダ実行例 (SQLの文法に近い表現)
SQLAlchemyのテーブル定義と実行
2020/8/28 31
import sqlalchemy as sa
metadata = sa.MetaData()
person = sa.Table(
'person',
metadata,
sa.Column('id', sa.Integer),
sa.Column('name', sa.String),
)
>>> stmt = sa.select([person]).where(person.c.name.contains('a'))
>>> print(stmt)
SELECT person.id, person.name
FROM person
WHERE (person.name LIKE '%' || :name_1 || '%')
32. モデル定義 (テーブル定義は自動(個別定義も可能))
クエリ実行例 (一般的なORMの構文に近い表現)
SQLAlchemyのモデル定義と実行
2020/8/28 32
import sqlalchemy as sa
from sqlalchemy.ext.declarative import declarative_base
metadata = sa.MetaData()
Base = declarative_base()
class Person(Base):
__tablename__ = 'users'
id = sa.Column(sa.Integer, primary_key=True)
name = sa.Column(sa.String)
>>> from sqlalchemy.orm import sessionmaker
>>> Session = sessionmaker()
>>> stmt = session.query(Person).filter(Person.name.contains('a'))
>>> print(stmt)
SELECT person.id AS person_id, person.name AS person_name
FROM person
WHERE (person.name LIKE '%' || :name_1 || '%')