【図解】SQLAlchemyのfirst, one, scalarの違い
SQLAlchemyにはallをはじめとして多くのクエリーが存在します。その中でもfirst, one, scalarは動作が似ており、きちんと区別して使えている人は少ないです。
そこでこの記事では各クエリーの違いを解説します。
first
クエリによって取得できる全ての行のうち先頭の行だけを取得します。もしクエリによって一つも行が取得できなかった場合にはエラーは発生せず、Noneが返されます。
クエリが2つ以上のテーブルを結合するものであれば、結合された状態の行が一つだけ返ってきます。
使い所
order_by句を使ってあるカラムの並びによって並び替えた後に先頭の行を取得したい時などに使えます。例えば、レコードの作成順番でのソート(並び替え)とあわせてfirstを使うことで、最も最近作成されたレコードのみを取得することができます。
one
一行だけの取得を期待し、一行だけを取得するためのクエリです。
もしクエリによって一つも行が取得できなかった場合には"NoResultFound"エラーが発生します。複数行返された時には"MultipleResultsFound"エラーが発生します。
使い所
必ず取得対象が一つしか存在しないようWHERE句(SQLAlchemyで言うfilter)で絞りこむことと同時に使うことが多いです。
また、一つも行を取得できなかったときや複数行取得した時にエラーを発生させる性質からテスト用コードにもよく使われます。
scalar
クエリによって最初に取得できた行の最初の要素を返します。複数行取得できてしまったらoneと同じく"MultipleResultsFound"エラーが発生します。
取得結果がまだ少し分かりにくいと思いますのでscalarを使って値を取得した結果の例を公式サイトから抜粋します。
>>> session.query(Item).scalar()
<Item>
>>> session.query(Item.id).scalar()
1
>>> session.query(Item.id).filter(Item.id < 0).scalar()
None
>>> session.query(Item.id, Item.name).scalar()
1
特に注目して欲しいのが最後の例です。コードではItemテーブルのidとItemテーブルのnameを指定していますが、最初に指定された要素はid(Item.id)のため、1という値のみが返されています。
これは行として情報を取得するone, firstと要素を取得するscalarの大きな違いです。
ついでにoneと比較してみましょう。
# 注意:idカラムとnameカラムを持つUserテーブル作成してから以下の作業を行っています。
user_id = session.query(User.id).one()
print(user_id) # 出力結果:(1,)
print(type(user_id)) # 出力結果:<class 'sqlalchemy.util._collections.result'>
user_id = session.query(User.id).scalar()
print(user_id) # 出力結果:1
print(type(user_id)) # 出力結果:<class 'int'>
user = session.query(User.id, User.name).one()
print(user) # 出力結果:(1, 'ed')
print(type(user)) # 出力結果:<class 'sqlalchemy.util._collections.result'>
user = session.query(User.id, User.name).scalar()
print(user) # 出力結果:1
print(type(user)) # 出力結果:<class 'int'>
使い所
scalarは要素を返します。そのため、ある行のある要素を一つだけ取得したい場合に利用すると、取得後の記述をシンプルにできます。
全体まとめ
3つのクエリの違いをイメージすると以下のようになります。
複数行が取得できてもエラーを起こさず先頭行だけ返すfirst。
取得行数が0でも複数でもエラーを発生させてしまう厳しいone。
行ではなく要素を返すスマートなscalar。
それぞれのクエリの特徴を理解し、適切に使い分けたコーディングをこころがけましょう。