3.6. Set¶
Setはレコードセット定義のクラスです。さらに定義したレコードセットに対する、レコードの取得・更新・削除といったメソッドも 持っています。
Setクラス — レコードセット定義に関するクラス | |
---|---|
レコードセット取得 | |
レコード件数取得 | |
レコード存在チェック | |
レコードの更新 | |
レコードの更新 (コールバック関数の無視) | |
バリデータとレコード更新 | |
レコードの削除 | |
ネストしたSELECT | |
SQL文生成 | |
SQL文生成 | |
SQL文生成 | |
SQL文生成 |
3.6.1. インスタンス化¶
DAL インスタンスを関数として呼ぶと、Setインスタンスを返します。これでレコードセットを 定義したことになります。また Setインスタンスを生成した段階では、データーベースクエリは実行されません [1] 。
【 Setインスタンスの生成 】
- 生成・・・・・・ DALインスタンスを関数呼び出しします。
>>> set = db(db.person.id > 0)
DALインスタンスのパラメータには、 Query インスタンスを指定します。Queryインスタンスはレコードセットの条件になります。
Table インスタンスや Field インスタンスもパラメータに指定できます。 この場合は、Queryインスタンスへと内部で自動変換します。
>>> set = db(db.person) >>> set = db(db.person.name)
- 変換後の構文
>>> set = db(db.person.id > 0) >>> set = db(db.person.name != None)
Setインスタンスの作成ではDALインスタンスを使用しますが、この時次のパラメータを取ることが可能です。
ignore_common_filters
コモンフィルタで使用するパラメータです。詳細は コモンフィルタ を参照してください。
3.6.2. メソッド¶
メソッド説明用サンプルでは次のテーブルを利用します。
db.define_table('person', Field('name'), format='%(name)s') db.define_table('dog', Field('owner_id', db.person), Field('name'))
-
Set.
select
([*fields, **attributes]) → Rowsインスタンス¶ レコードセットのデータを取得します。
パラメータとしてフィールドやオプションを指定します。戻り値は Rows インスタンスになります。
>>> rows = db(db.person).select(db.person.id, db.person.name)
DALインスタンスのパラメータは省略することも可能です。
>>> rows = db().select(db.person.id, db.person.name)
- *fields
戻り値に含めるフィールドを Field インスタンスで指定します。 このパラメータは複数設定可能です。
Fieldインスタンスの代わりに、Tableインスタンスに ALL を付けて指定することも可能です。これは SQLALL クラスの インスタンスになります
>>> rows = db().select(db.person.ALL)
SQLALLインスタンスを指定すると、テーブルの全フィールドを指定したことになります。つまり次の構文と同等になります。
>>> rows = db(db.person).select()
- **attributes
オプションを指定できます。複数のオプションを指定可能です。設定可能なオプションは次の通りです。
- orderby
ソート順を指定できます。ソート指定での注意事項がいくつかあります。
- パラメータにはソートしたいFieldインスタンスを設定します。
- チルダ記号(~)をつけると逆ソートになります。
- <random> を指定するとソート順がランダムになります。
- 複数のフィールドでソートしたい時は、縦棒(|)でフィールド同士を連結させます。
>>> for row in db().select(db.person.ALL, orderby=db.person.name): ... print row.name Cosotama Pacedada Socepopa
>>> for row in db().select(db.person.ALL, orderby=~db.person.name): ... print row.name Socepopa Pacedada Cosotama
>>> for row in db().select(db.person.ALL, orderby='<random>'): ... print row.name Pacedada Cosotama Socepopa
<random>はGAEではサポートしていません。同様にサンポートしていない環境では、次のように記述すると同じ 結果を得ることができます。
>>> import random >>> for row in db().select(db.person.ALL).sort(lambda x :random.random()): ... print row.name Cosotama Socepopa Pacedada
>>> for row in db().select(db.person.ALL, orderby=db.person.name|db.person.id): ... print row.name, row.id Cosotama 3 Pacedada 1 Socepopa 2
参考: orderby, groupby, limitby, distinct | orderby, groupby, limitby, distinct(日本語)
- groupby
同じ値をグループ化します。パラメータにはグループ化するフィールドのFieldインスンタンスを指定します。
>>> for row in db().select(db.dog.ALL, groupby=db.dog.owner_id): ... print row.name, row.owner_id.name Comoceta Socepopa Cosasamo Cosotama
注意点としては、このオプションはGAEではサポートしていません。
- distinct
重複したデータを取り除きます。パラメータにTrueを指定すれば、このオプションが有効になります。
>>> for row in db().select(db.dog.owner_id, distinct=True): ... print row.owner_id.name Socepopa Cosotama
このオプションを有効にする場合はフィールド選択で、全フィールドを選択しない、もしくはidフィールドを除外することが 重要です。選択した場合、全レコードが重複無しの状態になるからです。
この他、distinctにブール値ではなく式を指定することが可能です。
>>> print db().select(db.dog.owner_id, distinct=db.dog.owner_id)
この場合、SQLには次のように変換されます。
SELECT DISTINCT ON (dog.owner_id) dog.owner_id FROM dog;
しかし、DISTINCT ON の構文は一部のデータベースでは動作しないようです。特に SQLite では動作しません。
- limitby
開始行と終了行を指定することで、テーブルデータの一部を切り抜くことができます。 パラメータは開始行と終了行で、タプルで指定します。またorderbyとも一緒に使用することは可能です。
>>> for row in db().select(db.person.ALL, limitby=(0,2)): ... print row.name Pacedada Socepopa
>>> for row in db().select(db.person.ALL, orderby=db.person.name, limitby=(0,2)) ... print row.name Cosotama Pacedada
- having
集計関数(メソッド)を用いた条件設定を行えます。これはSQL文のHAVING句と同じ動きをします。
>>> for row in db().select(db.dog.name, groupby=db.dog.owner_id, having='count(*) >= 3'): ... print row.name
このオプションはGAEではサポートしていないと思われます。
- join
内部結合(inner join)の設定を行います。結合させるテーブルのフィールドを *fields に指定する必要があります。 パラメータには、結合するテーブルのTableインスタンスメソッド on と、結合条件となるQueryインスタンスを設定します。
join = Tableインスタンス.on(Queryインスタンス)
on は
on()
メソッドです。 複数のjoinを設定することも可能です。その場合、リストもしくはタプル形式で指定してください。サンプルは、 selectメソッドでの内部結合定義 を参照ください。 なお
dal.Set._init__()
コンストラクタでも内部結合の設定が可能です。
- left
左外部結合(left outer join)の設定を行います。パラメータは joinオプションと同様に、結合するテーブルの Tableインスタンスメソッド on と、結合条件となるQueryインスタンスを設定します。
left = Tableインスタンス.on(Queryインスタンス)
on は
on()
メソッドです。 複数のleftを設定することも可能です。その場合、リストもしくはタプル形式で指定してください。サンプルは、 左外部結合 を参照ください。
参考: Left Outer Join | 左外部結合
- cache
取得したレコードセットのキャッシュを設定します。キャッシュを設定を行うと、システムのパフォーマンスが 向上します。
パラメータはタプルで渡します。タプルの最初の要素は、キャッシュモデルの指定です(cache.ram , cache.disk など)。 第二要素は、キャッシュの有効期限です(秒単位)。
def person_list(): rows = db().select(db.person.ALL, cache=(cache.ram, 60)) return dict(rows=rows)
キャッシュの有効期限は、キャッシュを保存した時ではなく、キャッシュの読み込み時の有効期限です。 つまりサンプルでは、最終のデータベースIOから、60秒以内だったらデータベースから取り出すことはなく、cache.ram から取り出します。 60秒を超えている場合、データベースから取り出すと共に、cache.ram にデータをキャッシュします。
- cacheable
cacheable を True に指定すると、検索結果の Rows がシリアライズ化されキャッシュされます。 デフォルトは False です。
True の場合、
update_record()
、delete_record()
といった組み込み関数は使用できません。 使用できなくでも問題がない場合、cacheableをTrueにするだけで、selectメソッドを高速化します。もしcacheableがFalseで、cacheが設定されている場合、データベースでの問い合わせ結果だけがキャッシュされます。 cacheableもTrueの場合は、検索結果のRowsもキャッシュされるため、非常に高速になります。
def person_list(): rows = db().select(db.person.ALL, cache=(cache.ram, 60), cacheable=True) return dict(rows=rows)
参考: Caching Selects | 選択のキャッシュ
- 戻り値
- Rowsインスタンスです。
参考: select | select(日本語)
-
Set.
count
([distinct]) → レコード数¶ Setインスタンスに含まれるレコード数を返します。
>>> db(db.dog).count() 14
- distinct
データ重複の制御を行うパラメータです。パラメータにFieldインスタンスを指定すると、指定したフィールドの データ重複を排除したレコード件数を返します。デフォルト値はNoneです。
>>> db(db.dog).count(distinct=db.dog.owner_id) 3 >>> db(db.dog).count(db.dog.owner_id) 3
- 戻り値
- レコード数です。
参考: count, delete, update | count, isempty, delete, update(日本語)
-
Set.
delete
() → 削除レコード数¶ Setインスタンスに含まれるレコードの削除を行います。
>>> db(db.dog.id >= 5).delete() 10
- 戻り値
- 削除したレコード数です。
-
Set.
isempty
() → ブール値(True/False)¶ Setインスタンスのレコードが空かどうかを返します。 True の場合は空です(レコードが存在しません)。
>>> db(db.person.name.startswith('A')).isempty() # Aで始まるレコードの存在チェック True >>> db(db.person.name.startswith('C')).isempty() # Cで始まるレコードの存在チェック False
- 戻り値
- True -> レコードが存在しませんFalse -> レコードが存在します
-
Set.
update
(**update_fields) → 更新レコード数¶ Setインスタンスに含まれるレコードの更新を行います。
>>> db(db.dog.id == 4).update(owner_id=1) # idが4のレコードに対し、owner_idフィールドの値を1に更新 1
- **update_fields
- パラメータ名にフィールド名をパラメータに更新値をセットします。複数設定が可能です。
- 戻り値
- 更新したレコード数です。
-
Set.
update_naive
(**update_fields) → 更新レコード数¶ コールバック関数 を起動しないで、Setインスタンスに含まれるレコードの更新を行います。
>>> db(db.dog.id == 4).update_naive(owner_id=1) 1
- **update_fields
- パラメータ名にフィールド名をパラメータに更新値をセットします。複数設定が可能です。
- 戻り値
- 更新したレコード数です。
-
Set.
validate_and_update
(**update_fields) → Rowオブジェクト¶ Setインスタンスに含まれるレコードに対して、レコード更新値をバリデータした後、更新を行います。 バリデータはフォームやSQLフォームで実施されるため、通常ではこのメソッドは使用しません。
>>> db(db.person.id==4).validate_and_update(name='a'*513) # バリデータエラー <Row {'updated': None, 'errors': <Row {'name': 'enter from 0 to 512 characters'}>}> >>> db(db.person.id==4).validate_and_update(name='a'*512) # レコード更新成功 <Row {'updated': 1, 'errors': <Row {}>}> >>> db(db.person.id==4).update(name='a'*513) # updateメソッドではバリデータを実施しないので更新される 1
nameフィールドはstring型のため、デフォルトでは512文字より大きい場合バリデータで制限されます。
- **update_fields
- パラメータ名にフィールド名をパラメータに更新値をセットします。複数設定が可能です。
- 戻り値
- Rowオブジェクトを返します。エラーがある場合は ‘errors’ の辞書値に、フィールド名がキーでエラーメッセージが値の辞書型データが設定されます。 更新成功時は、’updated’ の辞書値に更新レコード数が設定されます。
よく似た機能で、
validate_and_insert()
も存在します。
-
Set.
nested_select
([*fields, **attributes]) → Expressionインスタンス¶ select()
の実行結果をExpressionインスタンスにします。これにより、ネストされたDAL構文を作成できます。例えば次のように、Selectの実行結果を使って値を更新する場合、データベースに対して2度命令を発行する必要があります。
>>> person_id = db(db.person.name=='Nathanial Azzano').select().first()['id'] >>> db(db.dog.id==4).update(owner_id=person_id) 1
しかしnested_selectメソッドを利用すれば、ネストしたSelectを直接値に代入でき、データベースへの一度の命令発行でレコード更新が可能になります。
>>> person_id = db(db.person.name=='Nathanial Azzano').nested_select(db.person.id) >>> db(db.dog.id==4).update(owner_id=person_id) 1
ここで person_id の中身を見てみます。
>>> print person_id (SELECT person.id FROM person WHERE (person.name = 'Nathanial Azzano'))
SQL文になっています。これは遅延実行されることを示しています。
_select()
に似ていますが、nested_selectは Expression オブジェクトを返します。またnested_selectを使わない方法は、
first()['id']
を使っていることに注意してください。 これは通常のselect()
は Rows を返すためです。nested_selectを使用した方が、より実行効率が高いことが分かると思います。
-
Set.
_select
([*fields, **attributes]) → SQL文¶
-
Set.
_count
([distinct=None]) → SQL文¶
-
Set.
_update
(**update_fields) → SQL文¶
3.6.3. 関数へのエミュレーション¶
Setインスタンスを関数呼び出しした場合、パラメータの Query インスタンスが and 条件として追加されます。
>>> set = db(db.person.name.startswith('C')) # nameフィールドが C で始まるレコードセットの定義 >>> set = set(db.person.name.endswith('A')) # 最後が A で終わる条件を追加 >>> print set.select() person.id,person.name 4,Cecemosa 8,Codadama次のようにも記述できます。
>>> print db(db.person.name.startswith('C'))(db.person.name.endswith('O')).select() person.id,person.name 6,Cedumoco 7,CeduducoQueryクラスの 論理演算子 で記述すると次のようになります。
>>> print db(db.person.name.startswith('C') & db.person.name.endswith('O')).select() person.id,person.name 6,Cedumoco 7,Ceduduco
[1] | Pythonのinstance型オブジェクトではありませんが、簡単に説明するために インスタンス という言葉を使用しています。 |