こんにちは。
これは Delphi Advent Calendar 2017 最終日の投稿です。
最終日だというのに、おいらなんかがノコノコ出て来て良いのか?って話もありますが。
よろしくお願いします。
Firebirdで配列を使いたい。と思って挑戦した時の顛末です。
データロガーを作っているのですが、DoubleとBooleanの配列をテーブルに格納したかったというところから始まっています。
##環境
Delphi 10.2.2
Firebird 2.5
Windows10
の環境での話です。
##取り敢えず、データベースを作成して、テーブルを作成します。
isqlやFrameRobin等を使用して、次のテーブルを作成しました。
CREATE TABLE LOG_DATA(
LOG_NO INTEGER NOT NULL,
LOG_COUNT INTEGER NOT NULL,
GET_TIME TIMESTAMP NOT NULL,
SET_ANLG DOUBLE PRECISION[32],
SET_DGTL SMALLINT[32],
GET_ANLG DOUBLE PRECISION[512],
GET_DGTL SMALLINT[1024],
CONSTRAINT PK_LOG_DATA_ID PRIMARY KEY( LOG_NO, LOG_COUNT )
);
PRIMARY KEYはLOG_NO, LOG_COUNTとしておきます。
SET_ANLG, SET_DGTL, GET_ANLG, GET_DGTLの4項目。
型定義の後に[]
で添字が付けられているのが配列項目になります。
##Delphiから、 FireDACを用いてこのテーブルにアクセス
FDConnectionを置いて、Server, Database, UserName, Password等のパラメータをセット。
そしてFDQueryを置いて、FDConnectionを関連付けて、
FDQueryのSQLに
SELECT *
FROM LOG_DATA
WHERE LOG_NO = :LogNo AND LOG_COUNT = :LogCount
と設定。
実際の読み込みは、
FDQuery.ParamByName( 'LogNo' ).AsInteger := _LogNo;
FDQuery.ParamByName( 'LogCount' ).AsInteger := _LogCount;
FDQuery.Open;
とすれば、_LogNo, _LogCountで指定した行が返されます。
##さてさて、配列部分はどうアクセスすれば良いのでしょうか?
v := FDQuery.FieldByName( 'GET_ANLG' )[0].AsFloat;
みたいに書けるのかと思ってみたのですが、違うようです。
色々試してみました。
v := FDQuery.FieldByName( 'GET_ANLG[0]' ).AsFloat;
と書くと動きました。
でも、これだと、for文で回して配列内の全ての項目にアクセスする場合、
for i := 0 to 512 - 1
v := FDQuery.FieldByName( 'GET_ANLG[' + i.ToString + ']' ).AsFloat;
と、かなり冗長というか、何のための配列なの?となってしまいます。
##もっと違う方法があるはず
もっとスマートな方法が必ずあると思い、Helpを舐め回しました。
ありました!もう少しスマートなやり方が!
TArrayFieldのFieldValuesを使う方法です。
早速書き換えてみます。
for i := 0 to 512 - 1 do
v := TArrayField( FDQuery.FieldByName( 'GET_ANLG' ) ).FieldValues[i];
TArrayFieldにキャストして、FieldValuesにアクセスするというのが基本みたいです。
FieldValuesはVariantの為、AsFloatとか無くてもそのままDoubleが帰って来ます。(vがDoubleで宣言されている場合)
個人的にはVariantはあまり使わないのですが、背に腹は代えられません。。。
このFieldValuesはTArrayFieldのデフォルトプロパティなので
for i := 0 to 512 - 1 do
v := TArrayField( FDQuery.FieldByName( 'GET_ANLG' ) )[i];
でアクセスできました。
うん、少しはスマートになりました。
##もう一声って感じで。
for文で回す中で、毎回毎回FieldByNameが走るとなると、いくらDictionaryを使っているとはいえ、
パフォーマンスに難がありそうです。
そこで、
var
fld: TArrayField;
と宣言してfor文の前にfldに該当のTArrayFieldを取り出します。
すると、
fld := TArrayField( FDQuery.FieldByName( 'GET_ANLG' ) );
for i := 0 to 512 - 1 do
v := fld[i];
と、かなりスッキリしました。
##あとがき
みなさん、あまりDBで配列使わないみたいで、情報がネットにも殆どありませんでした。
そもそも、DBで配列がサポートされて、それをFireDAC等のミドルウェア(?)がサポートしたのも
最近っぽいのでいたしかたないのかと思いますが。
実際、上記テーブルに、1秒間隔でログデータを落としていますが、今の所、問題なさそうです。
これから、長期稼働テストとかしないといけませんけど。。。
#それでは、みなさま、よいお年をお迎えください。
今年もお世話になりました。
来年もよろしくお願いいたします。