2.10. 再帰的select

リレーションを定義したテーブルでは、リレーション先のテーブルデータに容易にアクセスが可能になります。 アクセス方法としては、 テーブル結合(join) 、フォーマットオプション( formatオプションの動作 )、そして、再帰的select があります。 もちろん通常のselect を使用してもアクセスは可能です。

Note

再帰的selectは、個々のレコード毎に一回のselectを発行するため非効率です。可能ならjoinを使用した方が効率的です。

再帰的select について説明していきますが、説明には次の2つのテーブルを使用します。

db.define_table('person',
    Field('name'), format='%(name)s')

db.define_table('dog',
    Field('owner_id', db.person),
    Field('name'))

1.参照フィールドを使用して、参照先テーブルのフィールド値を取得

再帰的selectはjoinを使わずに、リレーション先のテーブルにアクセスする方法です。利用方法は参照フィールドに参照先の フィールド名を記述するだけです。サンプルを示してみます。

>>> dogs = db(db.dog).select()
>>> for dog in dogs:
...     print dog.name, dog.owner_id.name       # dog.owner_id.name は person の nameフィールドにアクセス

dogs は Rowsインスタンス、dog は Rowインスタンスになります。dog.owner_id はReferenceインスタンスに なります。dog.owner_id.name は、Referenceクラスの特殊メソッドを利用して値を取得していることになります。

dogs -> Rows インスタンス
dog -> Row インスタンス
dog.owner_id -> Reference インスタンス
dog.owner_id.name -> Reference クラスの特殊メソッドで取得した値

Referenceクラスは、複雑になるのでこのドキュメントでは説明しません。

前のサンプルをjoinで書き直すと次のようになります。

>>> rows = db(db.dog.owner_id==db.person.id).select(orderby=db.dog.id)
>>> for row in rows:
...     print row.dog.name, row.person.name

同様に通常のselectを使ったコードも示します。

>>> dogs = db(db.dog).select()
>>> for dog in dogs:
...     print dog.name, db.person(dog.owner_id).name    # 関数objタイプのショートカットを使用

2.参照先テーブルから、参照フィールドがあるテーブルのフィールド値を取得

前のサンプルでは dog -> person のように、テーブルの参照フィールドを使って参照先テーブルの値を取得 しました。 この逆の機能、つまり person -> dog のように、参照先から参照フィールドがあるテーブルのフィールド値を 取得することもできます。

>>> for person in db(db.person).select():
...     for dog in person.dog.select():
...         print person.name, dog.name

personは Rowインスタンス、dogも同じく Rowインスタンスになります。それでは person.dog は 何かというと Setインスタンスになります。つまり person.dog は、 db(db.dog.owner_id==person.id) の ショートカットということになります。

person -> Row インスタンス
dog -> Row インスタンス
person.dog
-> Set インスタンス
db(db.dog.owner_id==person.id) のショートカット

注意点としては、この構文は同一の参照先テーブルに対して、複数の参照フィールドを持つ場合は対応できません。

構文は似ていますが、db(db.dog.owner_id==person.id) は join ではありません。db.dog.owner_id が person.id 値と一致する よう条件設定しているだけです。ショットカットを使わないで記述すると次のようになります。

>>> for person in db(db.person).select():
...     for dog in db(db.dog.owner_id==person.id).select():
...         print person.name, dog.name

参考: Recursive selects再帰的なselects