
ナッキー |
データベースの機能はいろいろ紹介していただきましたが、奥が深いんだなぁって実感しました。まだまだ知らないことがあるんだろうなぁ。 |
|
|

高橋先生 |
データベースにはいろいろな機能があって、使いやすく管理しやすくなっているんだ。今回は確実にデータを登録する方法を紹介するよ。 |
|
|
データの更新
いくつかデータを更新してみると、時々変になるんですよね。これって私がドジなせい?教えて、高橋先生!
ナッキー:ときどき登録したはずのデータが、なくなっちゃうんですよね。[保存]ボタンもちゃんと押したのにぃ~。 |
高橋先生:え?普通に登録すれば問題ないはずだけど、どうやったのかな? |
ナッキー:うーん?1つのレコードを編集して、1つのレコードを追加しました。あ、IDがダブってたのかも! |
高橋先生:もう1回同じことをやってみてもらえるかな? |
|
では、皆さんも一緒にやってみて、うまくいかないかどうか確認してみてください。InterBaseサーバーを起動します。Windows OSのスタートメニューで「Borland InterBase 7.5 Developer [instance = gds_db]」の「InterBase サービスマネージャー [instance = gds_db]」をクリックします。「ステータス(T)」欄に、「InterBaseをWindowsサービスとして起動する」にチェックが付いていないことを確認して、「InterBaseサーバーは停止中」となっていれば、[起動(S)]ボタンをクリックして起動します。「InterBaseサーバーは稼動中」と変化したのを確認して[閉じる](×)ボタンで終了します。
次にプロジェクトを開きます。Turbo Delphiを起動して、画面中央の「ホームページ」で「ProfileDB.bdsproj」を選択。もし一覧に表示されていなければ、ツールバーの[プロジェクトを開く(Ctrl+F11)]ボタンをクリックします。「プロジェクトを開く」ダイアログボックスから「ProfileDB.bdsproj」を探します。
そのまま、ツールバーの[実行]ボタンをクリックして、プログラムを起動します。DBグリッドコンポーネントからどれかひとつレコード(今回はIDが7のレコード)を選択して「名前」フィールドと「誕生日」フィールドを変更します。変更できたら、[追加]ボタンをクリックして、新しいレコードを作ります。表を参考にして入力します。
ID |
名前 |
ADDR_ID |
住所 |
誕生日 |
性別 |
ペットの有無 |
1 |
鈴木 太郎 |
6 |
東京 |
1985/06/06 |
男性 |
無 |
|
|
|
|
|
|
|
「ID」フィールドが既存のものとダブっているけど、そのまま入力します。実際にはうっかり間違って、IDがダブってしまったんです。だけど、今回は同じ現象を確認していただくために、わざと同じIDを使います。このように編集と追加ができたら[保存(S)]ボタンをクリックします。

図01 データの加工
ボタンをクリックするとすぐにエラーメッセージが表示されます。設定によってはエラーメッセージを表示しないようになっている場合があります。表示されなくても問題はありません。これはメッセージを読んで[継続(C)]ボタンをクリックすればいいんですって。英語のところは読めないから読み飛ばして、「メッセージ'データベースエラー:~~'」って書いてあるわね。データベースが悪いんだ。

図02 エラーメッセージ1
よし、[継続(C)]ボタンをクリック。またメッセージが出ちゃいました。今度は日本語多くてよかった。「メッセージ'レコードが見つかりませんでした。キーを指定されていません'」ってことね。

図03 エラーメッセージ2
日本語だけどよくわかりませんね。「レコードが見つかりません」って出てくるけど検索なんかしていないのになぁ。[継続(C)]ボタンで続けます。画面上ではちゃんと変更と追加がされていますよね。確認できたら、このままフォームの[閉じる](×)ボタンで終了します。そして、もう1度ツールバーの[実行]ボタンをクリックしてみます。あー、名前などを変更したレコード(今回はIDが7のレコード)は有効なんですが、追加したデータがなくなっちゃっているんです。

図04 再実行時のデータ
新たに追加したレコードがありませんね。「ID」フィールドを間違えたのがまずかったわね。IDがダブらないように、気をつけていても間違うことってありますよね。そうするとレコードがなくなっちゃうかもしれないってことかな?名前などを変更したレコードが有効になっていて、追加のレコードが無くなっていることを確認できたらアンケートプログラムを終了します。
高橋先生:メッセージの英語の部分を読み飛ばしては、もったいないな。よく読めばもう少しどういうことかわかるよ。まず、「例外」は覚えているかな?以前にやったけど忘れているかもしれないね。プログラムを動かす上でのエラーをTurbo Delphiでは「例外」と呼んでいる。例外をコード上で扱うために例外オブジェクトが作られるんだったね。もう一度読み返してみたい時は「第8回の計算機でエラーチェック」を参照してみよう。メッセージから「EDatabaseError」例外ができたことがわかる。例外クラスの種類からどんなエラーなのかがわかるよ。今回はデータベースのエラーということが既にわかっているけれど、皆目見当がつかないときには有効だ。 |
ナッキー:( ) の中の英語は難しくて全然読めません。 |
高橋先生:おおまかに訳すと「PROFILEテーブルのINTEG_15フィールドでプライマリ・キーかユニーク・キーに違反がある」ということだね。 |
ナッキー:プライマリ・キーがダブっていて違反があるのはわかっていますけど、「INTEG_15」って何ですか? |
高橋先生:「ID」フィールドのことだよ。IBConsoleでプライマリ・キーの設定をしたあと名前が自動でつけられていたんだ。今、IBConsoleで確認すれば、ついている名前がわかるよ。気になる場合はIBConsoleを起動して、「Local Server」、「PROFILE.DB」とダブルクリックしていってデータベースを開く。一覧の「Tables」項目をクリックしてから「PROFILE」テーブルをダブルクリックしてプロパティを開く。「Properties for: PROFILE」画面が表示されたら[選択された項目の変更]ボタンで「テーブルエディタ」ダイアログボックスを表示すると、付いている名前が確認できるよ。

図05 INTEG_15 |
ナッキー:へぇ、勝手に名前が付いちゃうんだ。じゃあ、2番目のメッセージはどういうことですか? |
高橋先生:データベースにレコードが見つからないというメッセージだったね。1番目のメッセージは値をチェックしているときのエラーメッセージで、2番目のメッセージは実際に登録しようとしているときのメッセージだ。プライマリ・キーが「1」のレコードを登録しようとしていたけれど正しいIDではないからエラーが表示されたんだ。「ApplyUpdates」メソッドの中でほかの手続きなどを呼んでいるので、複数のメッセージを表示しているよ。 |
ナッキー:どちらも追加がうまくできなかったことを表してたんですね。でも、どうして最初はフォーム上に追加したレコードが表示されたんだろう? |
高橋先生:DBグリッドに表示されているデータはデータベースを直接表示しているわけじゃないんだ。「ローカルバッファ」とか「ローカルキャッシュ」と呼ばれるデータベースのコピーのようなものを参照している。ローカルバッファはこれから追加するデータを格納する場所でもあるから、画面上には追加した後のレコードのように見えるんだ。重複したIDフィールドを持つレコードがローカルバッファに残っている限り、[保存(S)]ボタンをクリックするたびにエラーが表示されるよ。 |
ナッキー:ローカルバッファって場所に登録されている内容がDBグリッドに表示されていたのね。でもデータベースには間違ったレコードは登録しないようにしてくれているんだ。 |
高橋先生:エラーが起こるかどうか監視しておいて、エラーが起こったらデータベースは元の状態に戻すという機能が働いているんだ。 |
ナッキー:へぇ。うまくできなかった理由はよくわかりました。 |
|
暗黙的なトランザクションと明示的なトランザクション
エラーが起こるかどうか見張っておいてくれるなんて安心。これもデータベースの機能の1つなのかな?
高橋先生:そうだね。レコードを追加したり、変更したり、削除してデータベースに変化が起こる処理を「更新」と呼ぶ。1つの更新処理を見張ってくれているんだ。失敗したら更新前の状態に戻してくれる。 |
ナッキー:あれ?住所の変更は設定できたみたいだけど、IDフィールドのダブっちゃった追加のレコードはなくなっていますね。半分は更新が成功しているから、画面は更新前の状態じゃありませんでしたよ。 |
高橋先生:今回の場合、処理グループの単位が1レコードずつなんだ。だから[保存(S)]ボタンは1度をクリックしたけれど処理グループは2つあったということだね。1つの処理グループを「トランザクション」というんだ。このような動作は、自動的に設定されるので「暗黙的なトランザクション」と呼ばれている。 |
ナッキー:トランザクションって、なんだかカッコいい名前ですね。住所を変更したトランザクションは成功して、追加したトランザクションは失敗だったんだ。 |
高橋先生:例えば、暗黙のトランザクションは1つのレコードに対して1つ用意される。だから一度に100件のレコードを変更したり追加したりした場合、100個のトランザクションが作られる。そのつどエラーがないかチェックするし、エラーがあった場合はその処理はデータベースには反映しない。 |
ナッキー:100個もあると、なんだか忙しそう。失敗したレコードがどれか、すぐにわかるんですか? |
高橋先生:残念ながら、100個もあるとすぐにはわからないな。エラーのレコードがデータセットになって残るから、そこからたどることもできる。わかりにくい、というだけなら問題にはならないけれど、出金と入金など両方が成立しないと困る場合もあるよね。 例えば、Aという銀行からBという銀行にお金を振替える場合を説明しよう。Aから100のお金を出金するのは成功して、Bにお金を入金するときデータベースでエラーが発生してしまったとする。この場合、出した100のお金はAに戻されるべきだよね。でも1レコードずつ処理していたら、Aからは100が出金されて、Bには入金されないという状況になるんだ。

図06 入金と出金の処理 |
ナッキー:それじゃあ、エラーを処理できても困っちゃうんですね。 |
高橋先生:だから、明示的に更新処理グループの範囲を指定するんだ。それが「明示的なトランザクション」というものだ。処理グループはここから始めます、とコードに記述するんだ。 |
ナッキー:自動で設定されたトランザクションは使わないんですか。 |
高橋先生:さっきの銀行の例だと、A銀行の出金からB銀行の入金までを1つのトランザクションとすればいいんだ。Aの出金で失敗しても、Bの入金で失敗しても、出金も入金もしていない元の状態に戻すことになる。 |
ナッキー:じゃあ、明示的なトランザクションで100個の更新を1つの処理グループとすると、そのうち1つでもエラーがあったら、100個のレコード全部が元の状態に戻っちゃうんですね。 |
高橋先生:そうすれば、やり直すときも全部をやり直せばいいから処理が難しくないよ。更新するレコードの件数がすごく多いとそうはいかないから、全体の処理時間との相談だね。アンケートプログラムは全部やり直しても、それほど多くはないと予想できる。エラーが起きたら、[保存(S)]ボタンをクリックするまでの更新を全部やり直すことにしよう。 |
ナッキー:やった!アンケートプログラムにもトランザクションを使おうっと。 |
|
StartTransaction・Commit・Rollback
トランザクションはエラーがないかチェックする処理グループということまでは、わかってきました。今度はコードに記述する番ね。どのように書いたらいいのかな?
高橋先生:トランザクションのはじまりを示すには、TSQLConnectionコンポーネントの「StartTransaction」メソッドを使うよ。トランザクションを複数持つことができるので、それぞれを区別するために、どんなトランザクションかをパラメータにセットする。トランザクションの情報はTTransactionDesc型で持つことができるんだ。StartTransactionメソッドを処理する前にTTransactionDesc型の変数を用意して、プロパティをセットしてね。 |
ナッキー:なんだか難しい型がでてきましたね。トランザクションの情報を入れておくのね。 |
高橋先生:用意した変数にセットしてほしいプロパティは「TransactionID」プロパティと「IsolationLevel」プロパティ。TransactionIDプロパティは固有の番号を表す。今回は複数のトランザクションは使用しないから、適当に番号をつけていいけれど、複数ある場合は重複しない番号にしてね。IsolationLevelプロパティは、1つのテーブルに複数のトランザクションがあったとき、ほかのトランザクションのデータをどのように読み込むかを決めている。定数には「xilDIRTYREAD」「xilREADCOMMITTED」「xilREPEATABLEREAD」などがある。それぞれの詳細については、ヘルプのキーワード検索で「トランザクション,排他レベル」を参照してみよう。ここではよく使われる「xilREADCOMMITTED」を設定しておく。 パラメータ用の「td」変数の書き方はこんな感じ var
td : TTransactionDesc;
begin
td.TransactionID := 1;
td.IsolationLevel := xilREADCOMMITTED;
|
end; 次は、念のためStartTransactionメソッドを記述する前に、トランザクションがすでに始まっていないかチェックしてみよう。トランザクション中かどうかは、TSQLConnectionコンポーネントの「InTransaction」プロパティに入っている。「Ture」だったらトランザクションは始まっている。「False」のときだけStartTransactionメソッドを実行しよう。パラメータには、さっき用意したtd変数を使ってね。 if not SQLConnection1.InTransaction then
SQLConnection1.StartTransaction(td); |
|
さっそく、ここまで記述してみましょう。画面をコードエディタに変更します。次にbtnSaveClickイベントハンドラを探します。見つかったら「begin」の前にパラメータ用のtd変数を宣言します。処理は「sdsProfile.ApplyUpdates(-1);」の前に記述します。空行を挿入して、太字部分を追加します。
procedure TfrmProfileDB.btnSaveClick(Sender: TObject);
var
td : TTransactionDesc;
begin
td.TransactionID := 1;
td.IsolationLevel := xilREADCOMMITTED;
if not SQLConnection1.InTransaction then
SQLConnection1.StartTransaction(td);
sdsProfile.ApplyUpdates(-1);
end;
ナッキー:StartTransactionメソッドを実行した直後からトランザクションが始まるんですね。ひとまとまりの処理として認識されるんだ。あ!どこまでいったら終わりになるんですか? |
高橋先生:トランザクションの終わりには2種類あるんだ。まず、更新が成功して、ローカルバッファの内容をデータベースに書き写して終わる場合。次に、更新の処理が失敗して、データベースをトランザクションが始まる前の状態に戻して終わる場合。成功したときは「Commit」メソッドを使う。失敗したら「Rollback」メソッドを使おう。どちらもパラメータはStartTransactionメソッドのときと同様、TransactionDesc型の変数を代入する。 |
ナッキー:成功したかどうかは、どうすればわかるんですか? |
高橋先生:SQLなどを使って更新する場合は例外処理ブロックを使うのだけれど、アンケートプログラムでは「ApplyUpdates」メソッドを使って更新しているから例外処理ブロックは使えない。ApplyUpdatesメソッドの内部でエラーの処理をしてしまうから例外処理ブロックでは受け取れないんだ。エラーがあったとき、ApplyUpdatesメソッドにはエラーの個数が戻り値となる。だから戻り値が0より大きいときエラーがあった、とわかるんだよ。 |
ナッキー:じゃあ、if文を使えばわかるんですね。そういえば、戻り値は使わないといけないんじゃなかったっけ?今までは戻り値を受け取らなくても平気だったのね。 |
高橋先生:特殊な関数になっているものは戻り値を無視してもエラーにならないようになっているんだ。このApplyUpdatesメソッドもそうだよ。 |
|
では、コードの続きを記述してみます。btnSaveClickイベントハンドラでは、いったん「sdsProfile.ApplyUpdates(-1);」の行を削除します。戻り値が0のとき「Commit」メソッドを使います。そうでないとき「Rollback」メソッドを使います。太字部分を追加します。
procedure TfrmProfileDB.btnSaveClick(Sender: TObject);
var
td : TTransactionDesc;
begin
td.TransactionID := 1;
td.IsolationLevel := xilREADCOMMITTED;
if not SQLConnection1.InTransaction then
SQLConnection1.StartTransaction(td);
if sdsProfile.ApplyUpdates(-1) = 0 then
SQLConnection1.Commit(td)
else
SQLConnection1.Rollback(td);
end;
ここまでできたら、保存して実行してみますね。ツールバーの[すべて保存]ボタンをクリックし、次に[実行]ボタンをクリックしてプログラムを実行します。まずは、レコードをどれか選択して名前フィールドだけ書き換えます。次に[追加(A)]ボタンをクリックして、最初に追加したレコードをもう一度入力してみます。住所フィールドまででかまいません。
ID |
名前 |
ADDR_ID |
住所 |
1 |
鈴木 太郎 |
6 |
東京 |
|
|
|
|

図07 トランザクションを使った更新
[保存(S)]をクリックすると、エラーメッセージが表示されます。メッセージボックスは[継続(C)]ボタンで進めます。
表示も更新する
エラーになる更新なので、Rollbackメソッドが働いて元に戻るはずよね。って、アレ画面上は何も変わらないみたい。教えて、高橋先生!
高橋先生:エラーではない更新はローカルバッファには残っていないから表示の更新をすればデータベースにある情報と同じようになる。このように異なる内容だったものを同じ内容にすることを「同期を取る」というんだ。更新するのはTSimpleDataSetコンポーネントの「Refresh」メソッドだ。だけどエラーの場合はレコードが、ローカルバッファにデータが残っているんだ。ローカルバッファにデータが残っていては表示の更新もできない。だから、まずローカルバッファにある更新をキャンセルしてから、Refreshメソッドでデータベースと同期を取ろう。 |
ナッキー:データベースのほうは、名前の変更もレコードの追加もRollbackメソッドで元に戻っているけれど、表示が元に戻っていないだけなんだぁ。もう1度実行したら元に戻ってるかな?よーしやってみよう。わぁ、元に戻ってる。再起動も「同期を取る」操作の1つなのね。じゃあ、同期を取る処理を追加する場所はどこにしましょうか? |
高橋先生:Rollbackメソッドの次に記述すればいいよ。if文の中にコードを入れたいから、ブロック化しておいてね。 |
ナッキー:これで、Rollbackした状態をすぐにみることができますね。 |
|
実行中の場合は終了しておきます。表示をコードエディタにして、btnSaveClickイベントハンドラを探します。以下のように太字の部分を追加します。
procedure TfrmProfileDB.btnSaveClick(Sender: TObject);
var
td : TTransactionDesc;
begin
td.TransactionID := 1;
td.IsolationLevel := xilREADCOMMITTED;
if not SQLConnection1.InTransaction then
SQLConnection1.StartTransaction(td);
if sdsProfile.ApplyUpdates(-1) = 0 then
SQLConnection1.Commit(td)
else
begin
SQLConnection1.Rollback(td);
sdsProfile.CancelUpdates;
sdsProfile.Refresh;
end;
end;
追加できたら、保存して実行します。ツールバーの[すべて保存]ボタンをクリックしたら、そのまま[実行]ボタンをクリックします。先ほどと同じように、レコードを1つ選択して名前を変更します。[追加(A)]ボタンをクリックして、IDフィールドを「1」にしたデータを追加します。入力できたら[保存(S)]ボタンをクリックします。エラーメッセージが表示されますので、[継続(C)]ボタンをクリックして処理を進めます。プログラムを起動しなおさなくても、画面は元の状態に戻ります。
OnReconcileErrorイベント
ちゃんとトランザクションで[保存(S)]ボタンでの更新処理が、全部元に戻りました。でもレコードを登録するときに、何がいけなかったのかよくわからないわね。変更したレコードと追加したレコードのどっちが悪かったか、わかるといいのに。
高橋先生:エラーのとき起こる「OnReconcileError」イベントに処理を記述すればいいよ。これは、ApplyUpdatesメソッドなどでエラーがあったとき起こるイベントだ。エラーごとに起こるから、複数のエラーがあったとしても、それぞれでメッセージを出すことができるよ。 |
ナッキー:へぇ。でもif文の中で処理してもいいんじゃないですか? |
高橋先生:イベントにはパラメータが入ってくる。OnReconcileErrorイベントにはたくさんのエラー情報がパラメータに入ってくるんだ。せっかくだから使ったほうがいいね。 まずはエラーのレコードの内容が入っている「DataSet」パラメータ。エラーがあったレコードがデータセットの形になって入っているよ。エラーは1レコードごとだから、データセットになっていても1レコード分しか入っていない。 |
ナッキー:パラメータに入ってくるから調べやすい!DataSetパラメータからレコードを取り出すのってコンポーネントを使うんですか? |
高橋先生:あぁ、そうか。これまで項目コンポーネントを使って値を取り出していたんだよね。このままでは値が参照しにくいから、項目コンポーネントをつくろう。データセットから項目コンポーネント取り出す「FieldByName」というメソッドがあるんだ。パラメータはフィールド名。そして項目コンポーネントが取り出せたら、値を取り出す「AsInteger」プロパティや「AsString」プロパティなどを使ってフィールドの値を調べよう。型に合ったプロパティを使ってね。たとえば「ID」フィールドの値を調べるときは i := Dataset.FieldByName('ID').AsInteger;このようになる。 |
ナッキー:これで、エラーになったレコードがどれだかわかりますね。でも、そのレコードが追加だったか、変更だったか思い出せるかな? |
高橋先生:どんな種類の更新だったかは「UpdateKind」パラメータに入っているよ。それぞれの定数は以下を参照。 編集:ukModify 追加:ukInsert 削除:ukDelete では、これらを使って、それぞれ「編集エラー」などのメッセージを出すことにしよう。case文で分岐できるよ。そのあと、DataSetパラメータを使ってIDフィールドと名前をメッセージボックスに出せばいいね。 |
|
では、エラーメッセージを出すコードを記述します。表示をフォームデザイナに変更してsdsProfileコンポーネントを選択します。オブジェクトインスペクタ、イベントページの「OnReconcileError」を探してダブルクリックします。イベントハンドラが作成できたら、UpdateKindパラメータを使ったメッセージから作成します。case文を使うんだったわね。「case UpdateKind of」のあと[Enter]キーを押すと自動的に分岐の値が入力されます。
procedure TfrmProfileDB.sdsProfileReconcileError(DataSet: TCustomClientDataSet;
E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction);
begin
case UpdateKind of //ここで[Enter]キー
ukModify: ;
ukInsert: ;
ukDelete: ;
end;
end;
続いてメッセージボックスを表示します。太字部分を記述します。
procedure TfrmProfileDB.sdsProfileReconcileError(DataSet: TCustomClientDataSet;
E: EReconcileError; UpdateKind: TUpdateKind; var Action: TReconcileAction);
begin
case UpdateKind of
ukModify: ShowMessage('変更エラー');
ukInsert: ShowMessage('追加エラー');
ukDelete: ShowMessage('削除エラー');
end;
end;
次に、レコードの内容をメッセージボックスに表示します。メッセージの内容は変数に代入しておきます。Messageの文字から数文字とって「msg」変数とします。値だけが表示されてもわかりにくいので「ID:」と「名前:」のように見出しをつけたほうが親切ですね。「ID:」のうしろと「名前:」の前後には空白があったほうが読みやすくなります。IDフィールドはInteger型なので型変換関数でstring型にします。太字部分を追加します。
unit FormProfileDB;
interface
(中略)
implementation
uses FormSelect;
{$R *.dfm}
procedure TfrmProfileDB.btnAddDataClick(Sender: TObject);
begin
sdsProfile.Append;
end;
procedure TfrmProfileDB.btnDeleteClick(Sender: TObject);
begin
if MessageDlg('削除してもいいですか?',mtConfirmation,
[mbYes,mbNo],0) = mrYes then
sdsProfile.Delete;
end;
procedure TfrmProfileDB.btnNextClick(Sender: TObject);
begin
sdsProfile.Next;
end;
procedure TfrmProfileDB.btnPriorClick(Sender: TObject);
begin
sdsProfile.Prior;
end;
procedure TfrmProfileDB.btnSaveClick(Sender: TObject);
var
td : TTransactionDesc;
begin
td.TransactionID := 1;
td.IsolationLevel := xilREADCOMMITTED;
if not SQLConnection1.InTransaction then
SQLConnection1.StartTransaction(td);
if sdsProfile.ApplyUpdates(-1) = 0 then
SQLConnection1.Commit(td)
else
begin
SQLConnection1.Rollback(td);
sdsProfile.CancelUpdates;
sdsProfile.Refresh;
end;
end;
procedure TfrmProfileDB.btnSelectClick(Sender: TObject);
var
s : string;
begin
s := '';
frmSelect.ShowModal;
if frmSelect.ModalResult = mrOk then
begin
if frmSelect.edtID.Text <> '' then
s := '(ID = ' + frmSelect.edtID.Text + ')';
if frmSelect.edtFullName.Text <> '' then
begin
if s <> '' then
s := s + ' AND ';
s :=s + '(FULLNAME LIKE ''' + frmSelect.edtFullName.Text + '%'')';
end;
if frmSelect.cmbAddress.Text <> '' then
begin
if s <> '' then
s := s + ' AND ';
s := s + '(ADDRESS = ''' + frmSelect.cmbAddress.Text + ''')';
end;
if frmSelect.medtBirthday.Text <> '' then
begin
if s <> '' then
s := s + ' AND ';
s := s + '(BIRTHDAY >= ''' + frmSelect. medtBirthday.Text + ''')';
end;
if frmSelect.cmbMale.Text <> '' then
begin
if s <> '' then
s := s + ' AND ';
if frmSelect.cmbMale.Text = '男性' then
s := s + '(MALE = TRUE)'
else if frmSelect.cmbMale.Text = '女性' then
s := s + '(MALE = FALSE)';
end;
if frmSelect.cmbPet.Text <> '' then
begin
if s <> '' then
s := s + ' AND ';
if frmSelect.cmbPet.Text = '飼っている' then
s := s + '(PET = TRUE)'
else if frmSelect.cmbPet.Text = '飼っていない' then
s := s + '(PET = FALSE)';
end;
sdsProfile.Active := False;
sdsProfile.DataSet.CommandText :=