Edited at
DelphiDay 25

Firebirdで配列

More than 1 year has passed since last update.

こんにちは。

これは 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秒間隔でログデータを落としていますが、今の所、問題なさそうです。

これから、長期稼働テストとかしないといけませんけど。。。


それでは、みなさま、よいお年をお迎えください。

今年もお世話になりました。

来年もよろしくお願いいたします。