フィルターを使ったread multi recordsは結果セットを持ちませんがここで説明する activeTable は 結果セット( recordset ) を持ちます。 activeTable はデータベースへのアクセスを担当します。 recordset は結果の保持とソートやグルピングなどを担当します。 この2つのオブジェクトと要求を表現するクエリーオブジェクトでSQL文並の読み取りクエリーを実行できます。
activeTable recordset query オブジェクトなどのアクセサ get setメソッドは、setが省略されています。たとえば setIndex(V)ならindex(V)となります。また同時にSQL文のようなイメージにするために、*thisを返しメソッドチェーンをできるようにしています。 setIndexとreadをつなげると以下のように記述できます。(at は activeTable )
またクエリーで(qはquery) setWhereとsetAnd_でしたら
といったように記述できます。
では具体的にそれぞれのオブジェクトを説明します。最初にactiveTableです。
activeTable の主なメソッドは、 read() と join() 、 outerJoin() の3つです。それぞれのメソッドに query オブジェクトを渡すことで必要なレコードを指定します。
activeTable の生成または直後(COMの場合)にデータベースオブジェクト(またはデータベースマネージャ)とテーブル名を渡します。 以下はusersテーブルを指定して生成します。
activeTable では alias() メソッドで結果セットで扱うフィールド名に別名を付けることができます。例えば、"名前"という日本語のフィールド名を "name"という別名を付けます。
複数ある時は、必要な回数 alias() を呼び出してください。いくつでも指定できます。 aliasを指定した場合は、以降クエリーなどで使うフィールド名は別名で指定するようにしてください。
それでは、テーブルからデータを読み出してみましょう。 最初に、インデックスを指定します。インデックスを指定することで、サーバーサイドでのデータの仮想的な並び順が決まります。 多くの場合、その並びのある範囲を検索対象にします。まず、範囲の先頭はそのキー値を指定します。 キー値100以降が検索対象でしたら
マルチセグメントキーでも大丈夫です。keyValueにセグメント順にその値を指定します。
テーブル全体が検索対象でしたら、キー値の最も小さい値を指定します。intでマイナスを入れることが無ければ 0 を指定します。
でもテーブル全体を検索対象にする場合は、レコード数が増えるにつれパフォーマンスが悪くなっていきますので 実際の運用での最大レコード数を良く考えてインデックスの設計をしてください。 (これはSQLでも同じですけれど、Transactdで書くとテーブル設計のまずさがコードから解りやすくなります)
次は絞り込みです。絞り込みにはqueryオブジェクトを使います。 多くの場合、範囲の終了値を最初に指定します。
これで、id 100〜200のレコードが取得対象になります。 さらに、groupidが2〜5として絞り込むには
と書きます。簡単ですね。 では読み取ってみましょう。
rsはレコードセットで結果セットです。ほんの2 3行のコードで読み取りできます。 各フィールドの値を読み出すにはrecordsetをrow colの2次元配列のようにアクセスできます。
colはインデックス番号とフィールド名のどちらでも指定できます。 列定義は rs.fieldDefs()でアクセスできます。
まとめとしてここまでの全コードと、列名と各行の値をすべてpirntする完全なコードです。
ここまでが基本です。これ以降はもう少し複雑なクエリーを説明しましょう。
先ほどの結果セットに別テーブルの関連するレコードをjoinしてみましょう。 まずは、blongsToです。userはgroupに属していてuserテーブルのgroupidフィールドがgroupsテーブルのcodeに関連づけられているとします。 また、codeはユニークキーでキー番号0とします。
最初に、groupsテーブル用の activeTable を作ります。 query オブジェクトは使いまわしで、最初に reset() を呼び出します。 フィールドはnameだけで良いので、select("name")でフィールドを絞ります。atg.index(0)でインデックスを指定しjoinに取得済のrecordset、queryとrecordset内のキーとなるフィールド名"groupid"を渡します。これでグループ名がjoinされました。
joinの場合無効なgroupidがあるとその行は無くなりますが、outerJoinを使うとnameフィールドは空のままでその行を残すことができます。 但し、その場合nameが解決できない長さゼロの文字なのか解決できた結果、長さゼロの文字であるかの判別はできません。
今度は逆に、hasManyをしてみましょう。 グループには複数のユーザーが所属しています。code=1に所属するユーザーを取得する例を示します。 まず、code=1のレコードを取得します。
次に、code=1に属する複数のuserをを取得します。
ポイントは2つあります。optimize(queryBase::joinHasOneOrHasMany)はこれから行うjoinが1対多のhasManyであることを明示します。 index(1)はgroupidフィールドのキーを指定します。このキーは非ユニークキーです。
最後にhasOneのJoinですが、これはblongsToとほとんど同じです。唯一異なるのは、optimizeにqueryBase::joinHasOneOrHasManyを指定してください。これを指定すると、レコードセット内の各行のJoinするキー値がすべて異なるユニークな値であるとマークされます。マークされると、サーバーに要求するキー値に重複値がないか探してまとめる処理をバイパスします。これにより、パフォーマンスが向上します。
では次に結果セットをソートして見ましょう。
これで電話番号順ですね。
これで電話番号順の降順です。
これで、電話番号順で同じ番号はname順になります。ソートしたい列名を順に列挙するだけです。
列ごとに昇順、降順を指定したい場合はsortFieldsを使います。
簡単ですね。
次は、グルーピング。グルーピングでは同時に簡単な計算もできます。sum avg count min max関数が用意されています。 では、groupidでグルーピングしgroupidごとにその数をcountしてみましょう。
c("count")は計算の結果を"count"という名前の列に格納することを意味します。
複数のフィールドを組み合わせてグルーピングする場合は
のように指定できます。
addFunctionでは1回のグルーピングに複数の関数を追加できます。 例えば、伝票明細の合計金額の計算で区分Aの商品の金額合計とそれ以外の合計を出すには
とすると、"区分A合計"列と"区分A以外合計"列が追加されてそれぞれのgruopごとの合計金額が入ります。 whenで条件を指定すると、条件にマッチしたレコードだけが計算の対象になります。
次は結果セットのフィルタリングです。結果セットをgroupid=1だけに絞ってみましょう。
これで、groupid=1の行だけが残ります。
最後はその他のrecordset操作です。 まず、コピー
先頭10行だけをコピー
先頭行、最終行
結合(Union)、unionは2つのrecordsetの列がすべて同じでなければなりません。異なる場合は例外がスローされます。
解放、clone()やnewで生成されたrecordsetは最後にrelease()を呼び出して解放してください。DLL内のメモリマネージャが正しく解放します。
のように、セグメント順にレコードセットのフィールド名を指定します。また、[]で囲むと固定値で指定することもできます。 例えば、伝票ヘッダーと明細があって、明細のキーは id + row number でidは伝票ヘッダーと関連付けするid, row numberは行番号だとします。各伝票の先頭の明細行 row number = 1だけを取得したい場合は Join(rs, q, "id", "[1]")とすると各ヘッダーに明細の先頭行だけJoinできます。
SQLでは、結果セットは1つですので、合計と明細を得るには2回似たようなクエリーを投げるか自前でグルーピングしなければなりません。 しかし、Transactdでは途中結果を簡単にコピーできます。
とすることで、rsに合計の結果セット、rs2に明細の結果セットと2つの結果を簡単に得ることができます。 とても効率良く処理できます。
recordset::join を使って recordset 同士をJoinすることができます。結合条件は recordsetQuery を使用します。以下の例を見てください。
上記の例は、rs.group = rs2.id で結合します。
whenの最初のパラメータに結合元の列名、3番目に結合先の列名を指定します。 条件は = だけでなくすべて使用できます。また複数の条件の指定も可能です。