MyBaseを試してみる。(フィルター編)TClientDataSetは、SQLが使えないもののFilterプロパティを設定することで、フィルター処理ができます。このFilterプロパティ・・・昔は、ヘルプの通り動作しなかった記憶がありますが、今では問題なく動作しています。MyBaseの場合、今までOnFilterRecordでごちゃごちゃ書いていた処理も、計算フィールドと組み合わせるとシンプルな処理にできます。今回のサンプルも、いつもように「コテコテなコード」で「コテコテなフィルター条件」ですが、MyBaseの性質上、数万のレコードであっても瞬時にフィルター処理されます。使えるフィルター条件式は、ヘルプを参照して下さい。文字列の比較用文字列を作成します。半角カタカナ、全角カタカナは、全角ひらがなとします。フィルター処理をします。固定リンクTweetMyBaseを試してみる。FireMonkey)VCLで試したMyBaseのグループ化をFireMonkeyでも試してみました。FireMonkeyでもVCLと同じようにMyBaseが使えそうですね。アプリケーションを選択します。各コンポーネントを配置し、オブジェクトインスペクタで次の設定をします。2.TPanelを配置します。alTop;3.TStringGridを配置します。alClient;4.TEditとTLabelを2つずつ配置します。'合計金額';5.TClientDataSetを配置します。6.TDataSourceを配置します。ClientDataSet1;7.TBindScopeDB1を配置します。DataSource1;8.TBindingListを配置します。9.BindDBGridLink1を選択し、オブジェクトインスペクタで次の設定をします。StringGrid1;下図のようにBindingsListを右クリックして、TBindDBGridLinkを追加します。(バインディングコンポーネントをクリックします)図のようにBindDBGridLink1の設定をします。(DataSource&GridControl)10.TBindDBEditLinkを2回追加し、オブジェクトインスペクタで次の設定をします。サンプルデータを追加します。CreateDB(ClientDataSet1);end;end.実行します。StringGrid1のオブジェクトインスペクタでAlternatingRowBackgroundをTrueにするとこんな感じになります。固定リンクTweetMyBaseを試してみる。項目とバイナリファイルとの間でのバイナリデータのコピーをサポートするメソッドを新しく導入します。項目によって管理されるデータを読み書きすることもできます。いうことで、次のようなコードを書いてみました。end;end;これらを使って次のような処理で、読み書きは確かにできます。cdsGetJPEGImage(ClientDataSet1,'IMG','D:\Test\aaa.jpg');end;しかし、たくさんの画像形式があるときに、いちいちBitmap用とかPNG用とか作るのは大変ですよね。SaveToFile等という関数がありました。これは使えると思い、早速試してみたところ、ファイル形式に関係なくとても簡単に扱えるようになりました。固定リンクTweetMyBaseを試してみる。REVENGE(^o^)前回、MyBaseを試してみる。処理では、フィルターが使えなかったのですが、次の処理をフィルター前、挿入処理前に入れることにより、フィルターも可能になりました。I;end;前回の処理では、一度レコードを挿入してから、フィルター処理・解除をすると、それ以降挿入したレコードは、最後に追加されてしまいました。スクリーン上で、正しく表示されていたとしても、正しくファイルに書き込むことができませんでした。今回の処理では、二度目以降のフィルター処理後も正しく表示されています。FilterプロパティでもOnFilterRecordでも問題ないようです。レコードを挿入します。フィルター処理します。フィルターを解除して、再びレコードを挿入します。フィルター処理します。フィルターを解除しても、レコードの並びは保持されています。別に要らない処理です。変更があれば更新させます。Insertで戻す場合、最後に追加されることをあるため、この処理を行う。cdsUpdateを前に持ってきてね。Deltaから順番に戻していきます。Count分空白行を追加します。今回追加した空白行の先頭レコードに移動させます。ClientDataSet1で作成しておきます。固定リンクTweetMyBaseを試してみる。(グループ化)TClientDataSetのグループ化については、Helpに解説がありますが、これが私にとって大変難解な文章です。図を見ていると、できることはわかるのですけど、では実際どうしたらいいのかがさっぱり理解できません。Wikisインデックスを使ってデータをグループ化するとりあえず試してみました。グループ化を使わずに普通にプログラムするとこんな感じです。サンプルデータを追加します。CreateDB(ClientDataSet1);end;同じデータがある場合、普通に表示するとDBGridでは、次のように表示されます。グループ化すると同じデータは、一度だけ描画されるだけになり、見やすくなるということで、次のように修正しました。空のセットを返します。'';end;各レコード毎に表示されていたITEMがすっきりとしています。Studioを選んだ場合と異なったアイテム数、合計金額が表示されます。このことから、同じITEMに対して処理がされていることがわかります。KINDフィールドも同じものが並んでいるので、こちらもグループ化させるために次のようにプログラムを修正しました。'';end;これでKINDもグループ化された表示になりました。ITEM数、合計金額についてもKINDに対して処理がされています。ここでようやくヘルプの「グループ化レベルは,インデックス内の項目順序に対応しています。固定リンクTweetMyBaseを試してみる。(PreviewHandlerで表示)と書いていますが、今回は、MyBaseとは直接関係ないです(^-^)以前、PreviewHandlerで自分のデータをExplorerで表示したいなと思って、下記サイトのサンプルをダウンロードし、試したことがありました。Delphi2009のShlObjにはIPreviewHandlerが定義されておらず、コンパイルできませんでした。今回、XE2を使ってMyBaseのデータをListViewで表示させてみました。サンプルのMyPreviewHandlerというユニットを次のものに入れ替えます。(MyBaseのファイルは、mbsという拡張子で保存しています。Wayこんな感じで、表示することができます。項目部分をクリックすると次のようなエラーが表示されます。'.mbs');end.コンパイルしてできたDLLをregsvr32を使って登録します。固定リンクTweetMyBaseを試してみる。)レコードの順番にインデックスを使って、行の編集をするバージョンです。言うまでもなく、インデックスは検索や目的に合ったレコードの並び替えをするために、取り扱うデータを分析し、その内容によっては、新規にカテゴリを作ってそのIDを割り当てたり、個別にコードを割り当てたりして作成されるべきものです。ここでは、各レコードの順番を登録するフィールド(ここではORD)を用意し、そのインデックス(ここではORD_IDX)を利用してレコードの順番を保持します。そのため、場合によっては、一度に全レコードのORDフィールドを書き換えるという禁断の処理です(^-^)プロの方々から「データベースの使い方を知ってるのか?」と怒られそうですが、「レコードの並びは、ユーザーが決める」「レコード数はせいぜい数千から1万件程度」を前提条件としています。サンプルデータを追加します。追加/挿入項目用の通し番号を返します。変更があれば更新させます。//フィルター中と他のインデックスの場合は処理させません。空白のままだとレコードが追加されません。今回追加した空白行の先頭レコードに移動させます。//フィルター中と他のインデックスの場合は処理させません。挿入位置以後のレコードのORDをCount分増やして更新させます。カーソルを挿入位置に戻します。取り扱うデータ量によっては、高速化のため計算部分の処理をさせない。削除対象を削除します。フィルターやインデックスに関係なく選択されたものを削除します。あるため、その対応です。すれば、このエラーは発生しない。選択されたままになるのでクリアしておきます。ORDを通し番号に修正します。フィルターは解除しておきます。ORD以外のインデックスの場合は、一度ORDに設定します。本当はBMList[0]のレコードまでを処理するのがいいかな。レコード位置を移動させます。ClientDataSet1.UndoLastChange(True);end;レコードの順番を登録するフィールド(ここではORD)の番号を、10000毎にするとか、浮動小数点フィールドとかにして、挿入時には、その間の番号を設定すると、番号の打ち直しが減って、処理がより早くなるかも知れないですね。固定リンクTweetMyBaseを試してみる。(Index、FilterでAggregateが変!?)Index、Filterを設定するとAggregateの値がおかしくなります。どこか使い方が間違ってるのかな?よくわからないけど、Aggregateを使うときには、このことに注意が必要です。★Filterプロパティを使った場合その11.[ITEMでソート]を押すと、ITEMでソートされて、合計金額は表示されません。なりますが、表示されないなら別にいいけどー(^-^)2.[ORD_IDXでソート]を押すと、再度、合計金額が表示されます。3.フィルター[ON]を押すと、フィルター処理された合計金額が表示されます。4.ここで[ITEMでソート]を押すと、なんと合計金額は2倍になりました。5.驚いて[ORD_IDXでソート]を押すと、合計金額は3倍になりました。6.これはいけないと思い、フィルター[OFF]を押すと元に戻りました。その21.フィルター[ON]を押して、フィルター処理後の合計金額が正しいことを確認します。2.フィルター[OFF]を押して、合計金額が正しいことを確認します。3.[ITEMでソート]を押すと、なんと合計金額は2倍になりました。4.驚いて[ORD_IDXでソート]を押すと、合計金額は3倍になりました。5.再び[ITEMでソート]を押すと、なんと合計金額は、4倍になりました。6.もう一度[ORD_IDXでソート]を押すと、なんと合計金額は、5倍になりました。[ITEMでソート]、[ORD_IDXでソート]を交互に押すと、合計金額はとどまるところを知らないようです。(^-^)★OnFilterRecordを使った場合その11.[ITEMでソート]を押すと・・・ITEMでソートされて、合計金額は表示されません。3.フィルター[ON]を押すと・・・合計金額が表示されません。4.ここで[ITEMでソート]を押すと・・・合計金額は表示されません。5.驚いて[ORD_IDX]を押すと・・・合計金額は表示されません。6.これはいけないと思い、フィルター[OFF]を押すと・・・合計金額は表示されません。その21.フィルター[ON]を押すと・・・合計金額が表示されません。2.フィルター[OFF]を押して、合計金額が正しいことを確認します。合計金額は正しく表示されました。合計金額が消えてしまいました。3.[ITEMでソート]を押すと・・・合計金額は表示されません。4.驚いて[ORD_IDX]を押すと・・・合計金額は表示されません。5.[ITEMでソート]を押すと・・・合計金額は表示されません。6.[ORD_IDXでソート]を押すと・・・合計金額は表示されません。合計金額は表示されません。試した結果、インデックスを入れ替えず、Filterプロパティでフィルター処理すれば正常な動作をしているようです。フィルター時には、フィルター処理後の合計金額が表示されますし、解除後は、正しい合計金額が表示されました。OnFilterRecordでフィルター処理した場合は、正しく表示されませんでした。サンプルデータを追加します。プロパティは相互に排他的です。一方を設定するともう一方はクリアされます。固定リンクTweetMyBaseを試してみる。)前にも書きましたが、私がBDE(TTable)+Paradoxを使っていて一番便利だと思っていたところはインデックスを設定しなくてもAppendとInsertを使い分けることによって、レコードの順番が保持されるところでした。TListView)のところをDBGridを使うためだけのParadoxだったので、そんな点をメリットに感じていました。DBGridも複数行のDrag&Drop、Cut&Pasteを実装したりして、データベースというより表計算のようなソフトになっていましたけど。私はMyBaseにもそんな仕様?を求めていました。同じように動作するならTTable(BDE)との置き換えが簡単かも知れないと期待しながら、そしてメモリ内なんだからインデックスはあったら便利だけど、無くても動くよね~なんて気軽に考えていました。これがなかなか難しく、結論としてインデックスがない場合のフィルターには対応できませんでした。(フィルターについては、前回紹介したCloneCursor+2段DBGridで妥協しました)インデックスを使わないMyBaseで困ったところ等を挙げます。その1ClientDataSetは、Append、Insertメソッドを持っており、単純に追加、挿入の処理ができます。DBGrid上で、追加、挿入の処理が正しくできているように見えても、そのままファイルに保存して、再度読み込むと挿入も追加扱いになっています。MergeChangeLogメソッドを呼び出した後、ファイルに保存すると正しいレコードの順番で書き込まれます。その2ClientDataSetは、Deltaというプロパティを持っており、そこに変更されたデータを保存しています。そのため、元に戻す処理も簡単にできるようになっています。Insertで挿入したレコードやDeleteで削除したレコードは、最終行に戻されるだけです。その3フィルター処理し解除した場合、挿入したレコードは、追加扱いになっています。MergeChangeLogメソッドを呼び出して、ファイルに保存すれば正しく動作することから、フィルター前にいろんなことを試してみましたが、一度目は成功してもなぜか二度目以降は正しく動作しませんでした。(この記事の最後にどのような処理をしたか、書き残しておきますね)その4複数行の処理を元に戻すためにSavePointを利用しています。容は、一度に元に戻したいからです。そのため、元に戻す処理は1度しか使えない仕様でプログラムしています。別に要らない処理です。変更があれば更新させます。Deltaから順番に戻していきます。Count分空白行を追加します。今回追加した空白行の先頭レコードに移動させます。セル編集時は、BMList.Countは0です。その場合の処理は含まれていないです。入れてテストしたものです。この処理で動作すると考えた。最後に移動してしまいます。end;end;MyBaseを試してみる。REVENGE(^o^)もご覧下さい。固定リンクTweetMyBaseを試してみる。(CloneCursorでOnFilterRecord)次のようにDBGridを2段配置して、下段にフィルター処理されたレコードを表示させてみました。CloneCursorでClientDataSet2を設定し、更にClientDataSet2.OnFilterRecordを設定して、それによりフィルター処理されることです。end;end;しかしながら、いざ実行してみると正しくフィルターできませんでした。いうかClientDataSet1のOnFilterRecordのデータが表示されています。CloneCursorのヘルプを読んでみると、第2のパラメーターの設定によることがわかりました。XE2DBClient.TCustomClientDataSet.CloneCursor日本語ページを見つけられなかったので、ヘルプを読んでね(^-^)CloneCursor側のOnFilterRecordを使う場合には、次のようにすると動作しました。固定リンクTweetより以前の記事一覧MyBaseを試してみる。2011.09.11MyBaseを試してみる。