【画像付き】SQLAlchemyのfirst, one, scalarの違い

SQLAlchemyにはall()をはじめとしてたくさんのクエリーがあります。その中でもfirst, one, scalarは動作が似ており、きちんと区別して使えている人は少ないです。
そこでこの記事では各クエリーの違いを解説します。
記事を作成するにあたり以下のSQLAlchemyの公式サイトを参考にしました。
Query API — SQLAlchemy 1.3 Documentation
first
クエリによって取得できる全ての行のうち先頭の行だけを取得します。もしクエリによって一つも行が取得できなかった場合にはエラーは発生せず、Noneが返されます。
クエリが2つ以上のテーブルを結合するものであれば、結合された状態の行が一つだけ返ってきます。
使い所
order_by句を使ってあるカラムの並びによって並び替えた後に先頭の行を取得したい時に使えます。例えば、レコードの作成順番(created_at)での並び替えと同時にfirstを使うことで、最も最近作成されたレコードのみを取得することができます。
one
一行だけ取得するクエリです。
もしクエリによって一つも行が取得できなかった場合には”sqlalchemy.orm.exc.NoResultFound”エラーが発生し、複数行返された時には”sqlalchemy.orm.exc.MultipleResultsFound”エラーが発生します。
使い所
必ず取得対象が一つしか存在しないよう、WHERE句(SQLAlchemyで言うfilter)で絞りこむことと同時に使うことが多いです。
また、一つも行を取得できなかったときや複数行取得した時にエラーを発生させる性質から単体テストの作成時にもよく使われます。
scalar
クエリによって最初に取得できた行(first()の結果)の最初の要素を返します。複数行の情報が取得できてしまったらoneと同じく”MultipleResultsFound”エラーが発生します。
少し分かりにくいので具体例を公式サイトから抜粋します。
>>> 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のため、1というidの値のみが返されています。
これは行として情報を取得するone, firstとの大きな違いです。
ついでに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。
それぞれのクエリの特徴を理解し、適切に使い分けたコーディングをこころがけましょう。
ディスカッション
コメント一覧
まだ、コメントがありません