LoginSignup
8
0

More than 5 years have passed since last update.

Firebirdで配列

Last updated at Posted at 2017-12-24

こんにちは。
これは 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秒間隔でログデータを落としていますが、今の所、問題なさそうです。
これから、長期稼働テストとかしないといけませんけど。。。

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

今年もお世話になりました。
来年もよろしくお願いいたします。

8
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
0