delphiで組み込みデータベースを使うときの候補

調べた範囲では3つ

  • sqlite
  • firebird
  • Microsoft Access

それぞれのコネクションに使うコンポーネント

sqlite
delphi用のフリーのsqlite wrapper http://www.itwriting.com/blog/articles/a-simple-delphi-wrapper-for-sqlite-3
firebird
InterBaseのコンポーネント。IB~という名前が付いているコンポーネント。
Microsoft Access
ADOコンポーネントを使うことでmdbファイルに接続できる。

組み込みデータベースではあともう一つ、Microsoftのsqlserverもコンパクトタイプとかいう名前で組み込みデータベースとして使うことができる。使ったことはないがだいたいAccessのmdbファイルと同じような使い方だと思われる。visual studio から編集することでデータベースファイルを作成できたと思うが(他にもあるかもしれないが)、メリットはあまりよくわからない。

sqlite3とfirebirdどちらをdelphiで使うか

また迷ってきた。TStringGridを使うならsqliteでも別に構わないということに気づいた。そもそもfirebirdを使うことにしたのはDBGridにselect文の結果データセットを上手くバインドできなかったからで、DBGridを使わないということならsqliteを使うことを検討してもいいことになる。

sqliteのメリット

  • auto incrementが使える
  • データベースファイルをコードから作成できる
  • firebirdよりも高速(らしい)
  • データベースを管理するソフトウェアが豊富
  • 技術情報が多い

firebirdのメリット

  • varchar型が使える(DBGridを使うならそのまま表示することができる)
  • ユーザーとパスワードが設定できる
  • sqliteよりもデータ型が多い
  • delphiのinterbase系のコンポーネントとの親和性が高い

sqliteのデメリット

  • 文字列型がtext型(DBGridを使う場合は一工夫必要になる)
  • ユーザとパスワードの設定が無い
  • TStringGridにデータをバインディングする場合は、カラムの文字列の位置(左寄せ、右寄せ、中央)などの設定がないので自らコーディングしなければならない。(これ、TSTringGridを使う人には結構致命的)

firebirdのデメリット

  • データベースファイルを作成するのが面倒
  • SQLの文法がとっつきにくい
  • auto increment が無いのでトリガーとジェネレータを作成しなければならない

感想

高度なデータベース操作ができない私にとっては、auto incrementの有無は結構大きなポイントだったりする。ソフトウェアのDBをバージョンアップしたりするときにテーブルを追加する度にジェネレータとトリガーを追加しなければならないというのは、結構なストレスになる。気分的にはfirebirdよりsqliteを使いたいという方へ傾いている。sqliteのwrapperを使ったコーディングだと、ものすごく簡単にできてしまう。

TDBGridとTStringGrid

IBQueryやIBDatasetで開いたテーブルをそのまま表示するなら、DBGridでやれば簡単だが、select文を実行した結果のテーブルセットにちょっと手を加えようと思ったらStringGridを使うようになる。面倒だけれども

サンプルコード

var
i,r : Integer;
sql : String;

begin
  sql := 'select count(*) as rcount from item'
  IBQuery1.SQL.SetText(Pwidechar(sql));
  IBQuery1.Open;
  r := IBQuery1.FieldByName('rcount').AsInteger;

  StringGrid1.ColCount := 3;
  StringGrid1.RowCount := r + 1;
  i := 1;

  sql := 'select id,name,item_num from item';
  IBQuery1.SQL.SetText(Pwidechar(sql));
  IBQuery1.Open;
  while not IBQuery1.Eof do
  begin
    StringGrid1.Cells[0,i] := IBquery1.FieldByName('inputDate').AsString;
    StringGrid1.Cells[1,i] := IBquery1.FieldByName('acId').AsString;
    StringGrid1.Cells[2,i] := IBquery1.FieldByName('catId').AsString;
    Inc(i);
    IBQuery1.Next;
  end;

end;

感想

心配なのは速度。行数が少ないなら無視できるが、千行とか何万行とかなるとどうなるのか未知数。DBGridで表示するのも内部的には同じ処理ならいいんだけれども、わからないので不安。

ところで、TStringGridとTDBGridの違いはデータソースコンポーネントかどうかという点に気づいた。TStringGridにはDataSourceやDetaSetなどを設定するプロパティは無いので、自分で好きなように加工した文字列などを表形式に表示する場合に使うのではないだろうか。TDBGridの場合はその逆で、あまり融通がきかないような印象。SQL文を工夫するか、プロシージャなどで結果セットを作成したりして、その結果を表示するだけというような気がする。(でもないか。OnGetTextイベントやOnDrawCellイベントを使ったりすると割りと自由に加工することができるかもしれない)

Happy Hacking Keyboard Pro2 を購入した

一年前に購入したキーボードが調子悪くなってきたので新しいものを、と思ってHappy Hacking KeyboardのProfessional 2を購入した。約2万円也。久しぶりの英語配列のキーボードだけれども、これぞプログラミングをするためのキーボード!というわけのわからないテンションで注文してみたものの、使いこなせるだろうか。1年以上はもってもらわんと困る。以下所感。

  • Windowsの場合コマンドキーの意味がわからない
  • 右サイドにControlキーが無いのに気づいた(前のキーボードでは多用していたんでちょっととまどう)
  • Happy Hacking Keyboard のスイッチテーブルスイッチの入れ替えでDeleteキーをBackSpaceキーに、左サイドのコマンドキーをFunctionキーにそれぞれ入れ替えるようにスイッチをオン。
  • 背面のゴムが思ったより小さいので机に直に置いたら滑る。デスクマットが要る。
  • 見た目がプロっぽくていい。道具なんかなんでもいい、大事なのは腕だ。と思っていた時期(以下略

使い始めたばかりなのでやはり違和感がある。今こそ人間の環境適応能力の見せ所。・・・しかし、この英語配列のキーボードに習熟するということは、日本語配列のキーボードをこれからは使わないということを意味するのに気づいた(両方の配列を自在に使いこなすほど器用ではないので)。現在所持しているノートPCのキーボードは日本語配列のキーボードだし、持ってはいなけれどポメラというガジェットも日本語キーボード前提で作られているし、場合によっては職場などでは日本語キーボードしか使用したらダメなこともあるだろうし。日本人が日本で英語キーボードを使っていくにはハードルがいくつか存在するということに今になって気づいた。今更引き返すことはできないのでこのHHKBを使いまくるしかない。購入検討中の人はJPモデルの日本語配列版か、同じキータッチとされるrealforceを選択するのが無難かもしれない。このただのProfessional 2はすっきりしていてプロっぽいが、キー配列は独特でかなり人を選ぶ。

InterBaseコンポーネントでSQLを実行する方法

IBQuery

sql:= ‘select id,itemName from item’;
IBQuery1.SQL.SetText(Pwidechar(sql));
IBQuery1.Open;

IBSQL

sql:= ‘insert into item(id,itemName) values(100,”’test”’)’;
IBSQL1.SQL.SetText(PWideChar(sql));
IBSQL1.ExecQuery;

IBDataSet

sql:= ‘select id,itemName from item’;
IBDataSet1.SelectSQL.Text := sql;
IBDataSet1.Open;

どれを使えばいいのかわからない件

IBQueryとIBDataSetはIBDataSourceのdatasetプロパティに設定をすることができる。Openプロシージャでselect文の結果セットがDBGridにバインドされる。IBSQLというのはinsertやdeleteなどのSQLを実行するコンポーネント・・・という理解でいいのだろうか。delphi XE2ではdbExpressのコンポーネントを使うことが推奨されているらしいが、それにしても似たようなコンポーネントでしかも同じような使い道ができるものが多いため戸惑う。

どうでもいいけれどfirebirdはSQL文中の文字の値はシングルクォートで囲まないといけないのか。知らなかった。

下記のページがよくまとまってある。
Databases – Delphi/Firebird – SQL

IBQueryのサンプルコード

sql := ‘insert into table(id,name) values(1,’test’)’;
IBQuery1.SQL.SetText(PWideChar(sql));

IBDatabase1.Open;
try
IBTransaction1.StartTransaction;
IBQuery1.ExecSQL;
IBTransaction1.Commit;
except
IBTransaction1.RollBack;

IBDatabase1.Close;

IBSQLというコンポーネントもあるが、IBQUERYと大体同じような使い方でいける。挙動は微妙に異なるような。
いちいちトランザクションを設定しないといけないのが面倒ではあるけれども。

データベースを開いたり閉じたりというのは、普通、どういう感じで実装しているのはわからない。トランザクション毎に開いて閉じて、という風にやるのは行儀が良いといえなくもないが、冗長な気もする。アプリケーション実行時に開いて、終了時に閉じるというのは雑すぎるような気もするし。(強制終了した場合は閉じないままとなってしまう可能性があり、そうなるとどういう問題が発生するのかも検証しないといけないし。多分、それはそれほど大問題にはならないとは思うが。それよりもコミットとロールバックしないままというのが問題だと思う)

・・・後者を採用してみるか。

IBQueryとIBTransaction

—————————
デバッガ例外通知
—————————
プロジェクト Project2.exe は例外クラス EIBClientError (メッセージ ‘Transaction not assigned’)を送出しました。
—————————
ブレーク(B) 継続(C) ヘルプ
—————————

InterBaseのコンポーネントをテストで使っているのだけれど、トランザクションのコンポーネントをIBQueryにセットしないことにはSQLを実行することはできないみたい。たぶん。InterBaseコンポーネントではSQLを実行するにはトランザクションでコミット・ロールバックしなければならない、という仕様なら従うけれどもー。デバッグしていて思ったんだけれど、コミットもロールバックもしないでアプリケーションが強制終了するという事態に陥ったら、コミットできないままの処理はどうなってしまうんだろう。さっきのテストのようにDBが開かなくなるんじゃないかとビビってるんだけれど大丈夫だろうか。IBQueryのプロパティでauto commitとか無いのだろうか。

firebirdのテーブルがDROPできない

categoryという名前のテーブルを作成して、それを削除しようとしたら、

SQL> drop table category;
Statement failed, SQLSTATE = 42000
unsuccessful metadata update
-object CATEGORY is in use

というエラーが発生してテーブルが削除することができなかった。原因は、トランザクションでコミットしていない処理が残っていたから。
SQL> commit;
と入力してやってからdropを実行すると上手く行った。

と、思ったら、commitしてもテーブルを削除できない現象を確認した。CATEGORYオブジェクトは使用中である・・・ふむ。
firebirdのisqlを一旦quitコマンドで終了させて、再度起動してデータベースに接続してみたら、今度はdropコマンドが通った。う~ん、なんだこりゃ。

あ、わかった。isqlで繋げたまま、delphiのIDEでも繋げていたからおかしな現象が発生していた模様。めんどくさいけどisqlはその都度接続するようにしないといかんな。

Delphi : DBGridのデータが(WIDEMEMO)と表示される場合の対処法

組み込みデータベースのsqliteをADOで接続してselectしたテーブルをDBGridにバインドしてみたら、sqliteのTextフィールドの値が表示されないのでかなりハマった。WIDEMEMOって何?とか思っていろいろググってみたが解決方法はわからず。さんざん調べてみて解決方法はどうも2つあるらしい。

  • OnGetTextイベントでテキストの値を変換する
  • OnDrawColumnCellイベントで値を変換する

OnGetTextイベントでやるのが一般的なようだけれど、いや、そのイベントどこのコンポーネントにあるのかわからないんだけど。しょうがないのOnDrawColumnCellイベントの方でやることにする(このイベントはDBGridのイベントでに存在する)。で、コードは適当に外国のサイトにあるのを参考にしてみた。

procedure TmainForm.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect;
DataCol: Integer; Column: TColumn; State: TGridDrawState);
begin
if Column.Field.FieldName = ‘comment’ then begin
DBGrid1.Canvas.FillRect(Rect);
DBGrid1.Canvas.TextRect(Rect, Rect.Left, Rect.Top, Column.Field.Value);
end;
end;

コードの内容は、想像に任せる。sqliteのText型がダメなのか?それじゃあfirebirdで試してみたら上手く表示されるのかも・・・とか考えていたら別のDBでも同じようにWIDEMEMOで表示されませんとかそういうネタがあったのでsqlite固有の問題ではないのかもしれない。

delphiを使う限りにおいては、組み込みデータベースはsqliteよりfirebirdの方が相性はいいという情報がちらほらあるが、どうなんだろうか。firebirdはinterbaseが元になっていて、そのinterbaseはBorlandが開発していて、そのBorlandはdelphiを開発していた、だからdelphiとfirebirdは相性がいい、とか。速度の面ではsqliteの方が速いらしい。

念のためfirebirdも試してみる

調べてみると、DBGridに表示させるフィールドの型はvarchar型か、varhcarより大きな場合はmemo型として表示させることになる模様。long varchar型(text)だからこの表示の問題は発生しているので、text型しか扱えないsqliteは扱いにくい。試しにfirebirdでも同様にやってみることにする。

firebirdのインストール

Firebird日本ユーザー会
のダウンロードよりfirebirdのインストールタイプをダウンロード・実行する。バージョンは2.5を選択してみた。
64-bit Classic, Superclassic & Superserver
Firebird-2.5.1.26351_1_x64.exe

firebirdのユーザ作成

  1. firebirdをインストールしたらコマンドプロンプトを起動する。起動したらfirebirdのインストールディレクトリにcdコマンドで移動する
    C:Usersnakahira>cd "C:Program FilesFirebirdFirebird_2_5bin"

  2. binフォルダにあるgsec.exeを使用してfirebirdを利用するアカウントを作成する。
    C:Program FilesFirebirdFirebird_2_5bin>gsec -user sysdba -pass masterkey -add nakahira -pw test
    sysdbaはfirebirdで初期設定されている管理者アカウントで、masterkeyはそのパスワード。そして新しくnakahiraという名前のアカウントとそのアカウントパスワードをtestとして追加する、という意味。
  3. firebirdのデータベースファイルを作成する。スタートメニューのプログラムプログラムから先程インストールしたFirebird ISQL Toolを実行する。するとコマンドプロンプトみたいな画面が表示されるのでデータベースを作成するコマンドを入力する。
    SQL> create database 'C:test.fdb' page_size 8192 user 'nakahira' password 'test';
    これで、Cドライブ直下にtest.fdbというfirebirdのデータベースファイルが作成される。

firebirdにauto incrementが無い件

ここまでやってみて、今更ながらfirebirdにはフィールドにauto increment機能が無いことに気づいた。どうやって主キーとして設定したidの値をカウントアップするのか、と調べてみたらtriggerを設定してみんなやっているらしい。
How to create an autoincrement column?
テーブルが1つや2つならそれでいいかもしれないが、5つや6つやそれ以上の場合もtriggerを設定していかないといけないのだろうか。まいったな。

firebirdならvarchar型があるのでDBGridに問題無く表示させることができる

テストしてみたら問題無かった。ただ、firebirdっていろいろと面倒だ・・・。組み込みのデータベースならMicrosoftのAccessのmdbファイルを使った方が楽な気がしてきた。

Delphi デバッグで実行してもウインドウが表示されない場合の対処法

デバッグで実行してみても、起動せずにウインドウが表示されない場合の対処法。

  • 直接デバッグフォルダのexeファイルを実行してみる

dllやコンポーネントを追加とか削除したりした場合に、デバッグを実行!とやってみてもうんともすんとも言わない時がある。構文チェックしてみてもエラーはないみたいだが、なぜか起動することができない。そういう時はデバッグフォルダにあるexeファイルを直接ダブルクリックで実行してみることで、何が問題なのかわかる時がある。たいていの場合はdllが見つかりませんというのが表示される。