ナッキーの「Delphiでビジネスアプリ奮闘記」- 第4回 データベースのレコードをCSVファイルに保存する

By: Hitoshi Fujii

Abstract: 今後データベースを使って、データの追加や編集をするから、CSVファイルにも結果を反映させたいな。今まではCSVファイルからデータベースに変換したけれど、今度は逆にデータベースをCSVファイルに変換してみましょう。

Hide image
nacky_adv_75x83

ナッキー

データベースのデータをCSVファイルに変換できれば、データベース側を編集したときにも安心ね。CSVファイルのデータを、データベースのデータに丸ごと置き換えれば、データベース側だけに追加や削除があっても対応できます。


    前回のおさらい

前回はCSVファイルのデータを使ってデータベースのデータを書き換えてみました。書き換えの対象レコードを探すために、Filterプロパティに検索条件をセットして抽出しました。FindFirstメソッドでレコードの有無を調べて、見つかったらCSVデータのレコードを書きこんだわ。

さらに、データベースに新規ユーザーを追加をしました。SYSDBAは、管理者用ですから、実際のアプリケーションでは使わないようにしないとね。

    データベースからCSVファイルに書き出す

CSVファイルからデータベースへデータを移行することができたので、これからはデータベースを使ったアプリケーションを作成して、データベースに対して編集を行いたいですね。そうすると、データベースに最新データが入っていて、CSVファイルには古いデータが残ることになります。完全にデータベースに移行するまでは、データベース、CSVファイルのどちらも使えると便利です。

そこで、データベースのデータをCSVファイルに移してみましょう。データベースのデータを、丸ごと全部CSVファイルに写し取れば、データベースだけが更新されたときCSVファイルにも反映できます。データベースからCSVファイルへデータを移す作業をしている間は、CSVファイルを編集できません。

データベースのデータを1レコードずつ読み込んではCSVファイルに出力すればいいわね。CSVファイルに、すでに入っているデータを消す作業も必要です。一度に消せるいい方法はないかしら。ヘルプの「標準ルーチンと入出力」の欄を調べてみました。ファイル自体を削除するルーチンはあるけれど、データだけ削除するのは無理そうね。あ、新しいファイルを作成するRewriteルーチンがあったわね。たしか、ファイルがあっても新しいファイルに置き換えてしまうから、今回必要な空のCSVファイルが作成できます。

AssignFile(csvFile, 'C:\Delphi_App\顧客.csv');
Rewrite(csvFile);

データベースの読み込みや、CSVファイルへの書き出しは以前やったから思い出せば大丈夫。たしか、「Writeln」ルーチンを使うんだったわね。空の新しいCSVファイルを作成して、データベースのレコードを読み込み、CSVファイルへ書き出す、という手順で進めてみます。

まず、最初のレコードのフィールドを1つずつ読み出して、間に「,」をはさみながら1レコード分を文字列変数にため込みます。すべてのフィールドを読み終えたら、CSVファイルへ1行書き出して、次のレコードを読み込むということを繰り返します。ただし、最初の「ID」フィールドは、元々のCSVファイルには存在しない項目ですから、読み飛ばします。次の「COMPANY」フィールドは、レコードの先頭になり頭に「,」は不要です。データだけ代入して「Continue」を使用します。これより後ろのフィールドは「,」ではさみますからデータの代入の前に「,」を付けます。

先頭フィールドのデータ

, 後ろのフィールドのデータ

, 後ろのフィールドのデータ

...


Continueはループ文の中で使用し、記述してあるところから下の処理をしないで、次のループ処理に進みます。

      while dmdTransCSV.ClientDataSet1.Eof = False do
        begin
          for i := 1 to 5 do
            begin
              if i = 1 then
                begin
                  str :=
                    dmdTransCSV.ClientDataSet1.Fields[1].AsString;
                  Continue;
                end;
              str := str + ',' + 
                dmdTransCSV.ClientDataSet1.Fields[i].AsString;
            end;
          Writeln(csvFile,str);
          dmdTransCSV.ClientDataSet1.Next;

CSVファイルに書き出すにあたって、1行目は項目名にする必要があります。忘れないようにしないとね。

Writeln(csvFile,'社名,住所1,住所2,電話,HP');

では、新しいフォームを追加しましょう。今回は通常のウィンドウフォームでいいわね。[ファイル(F)]メニューの[新規作成(N)]サブメニューから「フォーム - Delphi for Win32(L)」を選択して、新規フォームを追加します。ボタンを1つ配置してCaptionプロパティに「DB to CSV」、Nameプロパティに「btnDBtoCSV」と入力します。フォームのNameプロパティは「frmMenu」とします。

Hide image
01DBtoCSVボタン追加

図01 [DBtoCSV]ボタンの追加

次に既存のユニットも使うので、「DMTransCSV」と「TransCSVForm」をuses節に追加します。

implementation
uses DMTransCSV, FormTransCSV;

設定できたら保存しておきましょう。ファイル名は「MenuForm.pas」とします。これから、配置したボタンにデータベースからCSVファイルに移すコードを記述します。

    フォームの呼び出し

現在のユニットは3つあります。最初に作成したfrmTransCSVフォームとdmdTransCSVデータモジュール、今回のfrmMenuフォームです。これらを必要な時に呼び出すのですが、現在は起動するとfrmTransCSVフォームとdmdTransCSVデータモジュールが自動的に呼び出されて、frmTransCSVフォームを手前に表示しています。

今回作ったフォームを最初に起動して、メニュー画面にしたいけれど、どうしたらいいかしら?高橋先生に聞いてみよう。

FROM: ナッキー

TO: 高橋先生

SUBJECT: 後で作ったフォームを最初に表示するには

高橋先生、

こんにちは、佐竹です。

今度はデータベースからCSVファイルに移す作業に入りました。新たに作成したフォームを起動した時に表示したいのですが、どのようにしたらいいでしょうか?やっぱり、最初に作ったフォームしか、起動時のフォームにならないのかしら?

よろしくお願いいたします。

佐竹

すると、

FROM: 高橋先生

TO: ナッキー

SUBJECT: RE: 後で作ったフォームを最初に表示するには

ナッキー、

こんにちは、高橋です。

起動時のフォームは、プロジェクトのオプションで選択できる。また、最初に作成したfrmTransCSVフォームは起動時に必要ないから、自動生成から外していいよ。必要になったら、frmTransCSVフォームを生成しよう。

Takahashi

とのこと。

プロジェクトのオプションって、アプリケーション名をつけたりした事があったわ。まず、[プロジェクト(P)]メニューの[オプション(O)...]を選択して、「TransCSV.exeのプロジェクトオプション」ダイアログボックスを表示します。左欄で「フォーム」が選択されていることを確認して、「メインフォーム(M)」を「frmMenu」に変更するんですって。これで、起動時にfrmMenuフォームが開きます。

Hide image
02プロジェクトオプション

図02 プロジェクトプロパティ

次に、frmTransCSVフォームを自動生成から外すんだったわね。「自動生成フォーム(A):」欄を見てみましょう。ここには、起動したときにメモリ上に読み込まれるフォームを表しています。frmTransCSVフォームは、次に作成する[CSV to DB]ボタンをクリックする時に生成することにします。「frmTransCSV」を選択して、中央の[>]ボタンをクリックします。「使用可能フォーム(F):」欄へ移動したことを、確認します。

Hide image
03設定後の画面

図03 設定後の画面

これで、起動時にfrmTransCSVフォームは存在しないことになります。このままではfrmTransCSVフォームが使用できないので、[CSV to DB]ボタンを作成しましょう。

プロジェクトツリーでMenuFormを表示して、ボタンを1つ追加します。クリックイベントにfrmTransCSVフォームを表示するコードを記述します。追加したボタンのCaptionプロパティに「CSV to DB」、Nameプロパティに「btnCSVtoDB」と入力します。

Hide image
04CSVtoDBボタン追加

図04 [CSVtoDB]ボタンの追加

OnClickイベントハンドラに、frmTransCSVフォームを生成してモード付きで表示するように記述します。フォームの生成は、フォーム名に「T」をつけた型でフォーム変数を用意しておきます。

var
  frmCSVtoDB : TfrmTransCsv;

ここでは、「TfrmTransCsv」型ということになります。次に用意したフォーム変数に、フォームのCreateメソッドを使って代入します。パラメータには、自身が破棄されるタイミングで所有するフォームを自動的に破棄してくれる「持ち主(Owner)」を指定します。通常はプロシージャの持ち主である「Self」を使用しますが、今回は自前で破棄を行うので「nil」で良いそうです。

  frmCSVtoDB :=  TfrmTransCsv.Create(nil);

次は、モード付きで表示します。モード付きは、一度表示したら閉じるまで他のフォームの操作ができないタイプです。生成したフォームを閉じたら、廃棄し忘れないように注意します。

var
  frmCSVtoDB : TfrmTransCsv;
begin
  frmCSVtoDB :=  TfrmTransCsv.Create(nil);
  try
    frmCSVtoDB.ShowModal;
  finally
    FreeAndNil(frmCSVtoDB);
  end;
end;

生成したフォームを破棄するには、閉じるフォームのCloseイベントのパラメータを使うこともできるんですって。

procedure TfrmTransCsv.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  Action := caFree;
end;

生成と破棄を同じイベント内で処理したほうが記述しやすいので、今回はFreeAndNilルーチンを使って破棄します。

2つのボタンのイベントハンドラを完成させましょう。

procedure TfrmMenu.btnDBtoCSVClick(Sender: TObject);
var
  csvFile : TextFile;
  i : Integer;
  str : string;
begin
  AssignFile(csvFile, 'C:¥Delphi_App¥顧客.csv');
  Rewrite(csvFile);

  try
    Writeln(csvFile,'社名,住所1,住所2,電話,HP');
    dmdTransCSV.SQLConnection1.Connected := True;
    try
      dmdTransCSV.ClientDataSet1.Open;
      while dmdTransCSV.ClientDataSet1.Eof = False do
        begin
          for i := 1 to 5 do
            begin
              if i = 1 then
                begin
                  str :=
                    dmdTransCSV.ClientDataSet1.Fields[1].AsString;
                  Continue;
                end;
              str := str + ',' + 
                dmdTransCSV.ClientDataSet1.Fields[i].AsString;
            end;
          Writeln(csvFile,str);
          dmdTransCSV.ClientDataSet1.Next;
        end;
    except
      on E : Exception do
        begin
          ShowMessage(E.Message);
          abort;
        end;
    end;
  finally
    CloseFile(csvFile);
    dmdTransCSV.ClientDataSet1.Close;
    dmdTransCSV.SQLConnection1.Connected := False;
  end;
end;

procedure TfrmMenu.btnCSVtoDBClick(Sender: TObject);
var
  frmCSVtoDB : TfrmTransCsv;
begin
  frmCSVtoDB :=  TfrmTransCsv.Create(nil);
  try
    frmCSVtoDB.ShowModal;
  finally
    FreeAndNil(frmCSVtoDB);
  end;
end;

これで、データベースだけを更新してしまっても、あとからCSVファイルも追加で更新できますね。そのためには、データベースのデータがすべて最新であるような運用をしなければなりません。もし、CSVファイルを更新した場合、必ずデータベースに反映させるよう心がけてください。ここでは、のちのちCSVファイルから完全にデータベースに乗り換えることを前提としています。これからは、データベースを編集するための画面を作成して、CSVファイルを直接編集するより使いやすくします。さあ、ますます本格的なデータベースアプリケーションをめざして、がんばらなくっちゃ。

Server Response from: ETNASC04