TClientDataSetでメモリが足りません

解決


z  2014-09-27 23:31:55  No: 46709

お世話になります。

Delphi2007、Windows7、Oracle11g環境で
TADOConnection→TADOTable と
TSQLConnection→TSQLTable→TDataSetProvider→TClientDataSet  を
利用してMS-AccessのテーブルをOracleにコピーするプログラムを作成しています。

下記のプログラムを実行してこれまでは問題なかったのですが、この度フィールド数および
レコード数がとても多いテーブルをコピーすることになり、同じように実行したところ、
コピー中に「メモリが足りません」と出るようになりました

下記仕様だとApplyUpdatesが実行されるまでメモリを使い続けると思い、
Self.adoTable.Next; の下の行に10000レコードPostするたびに
ApplyUpdatesを実行してみたのですが、これでは改善されませんでした。

ApplyUpdatesを実行しただけではRecordは消えていないため、
メモリが無くなるかと思い、ApplyUpdatesの後にclientDataSetのClose→Openを
追加したらきちんと動きましたが、これが本当に正しい対処かどうか自分には
判断つきませんでした。有識者の方はどのような対処が妥当であるか教えて頂けませんか。

  Self.sqlTable.Open;
  Self.clientDataSet.Open;
  // 全レコード削除
  Self.sqlTable.DeleteRecords;
  Self.adoTable.Open;
  Self.adoTable.First;

  // Accessのテーブルから1レコードずつ取得
  while not Self.adoTable.Eof do
  begin
    Self.clientDataSet.Append;
    // 全フィールドのValueをコピー
    for intCount := 0 to arySFields.Count - 1 do
    begin
      Self.clientDataSet.FieldByName(arySFields.Strings[intCount]).Value :=
      Self.adoTable.FieldByName(aryDFields.Strings[intCount]).Value;
      // ★★★★★ ここに仕掛けを追加してみた
    end;
    Self.clientDataSet.Post;
    Self.adoTable.Next;
  end;
  Self.clientDataSet.ApplyUpdates(-1);
  arySFields.Free;
  aryDFields.Free;
  Self.clientDataSet.Close;
  Self.adoTable.Close;
  Self.sqlTable.Close;

  Self.adoConnection.Close;
  Self.sqlConnection.Close;


z  2014-09-27 23:37:16  No: 46710

間違えていました。仕掛けを追加したのは↓です。
また、sqlTableはTSQLTable、adoTableはTADOTable、
clientDataSetはTClientDataSetです

  while not Self.adoTable.Eof do
  begin
    Self.clientDataSet.Append;
    // 全フィールドのValueをコピー
    for intCount := 0 to arySFields.Count - 1 do
    begin
      Self.clientDataSet.FieldByName(arySFields.Strings[intCount]).Value :=
      Self.adoTable.FieldByName(aryDFields.Strings[intCount]).Value;
    end;
    Self.clientDataSet.Post;
    Self.adoTable.Next;
    // ★★★★★ ここに仕掛けを追加してみた
  end;


HOta  2014-09-28 17:32:12  No: 46711

TClientDataSetはDataControlへ接続するものですね。直にTSQLTableのFieldへ代入するのはダメでしょうか?その場合、TSQLConnectionのトランザクションを利用します。
かなり単純になります。


z  2014-09-28 20:33:23  No: 46712

お世話になります。

dbExpressのTSQLTableのValueにはコピーできないものと
思い込んでいました。明日早速試してみます。
ご意見ありがとうございました。


z  2014-09-29 19:03:30  No: 46713

教えて頂いたような変更を行ってみたのですが、下記コードを実行すると
Append実行時に「書込み禁止のデータを変更できません」と表示されます。
TSQLTableのプロパティで注意すべき点とか御座いますか?

  dbxTrans:= Self.sqlConnection.BeginTransaction;
  ・・・・中略・・・・
  Self.sqlTable.Open;
  // 全レコード削除
  Self.sqlTable.DeleteRecords;
  Self.adoTable.Open;
  Self.adoTable.First;

  // Accessのテーブルから1レコードずつ取得
  while not Self.adoTable.Eof do
  begin
    Self.sqlTable.Append;
    for intCount := 0 to arySFields.Count - 1 do
    begin
      Self.sqlTable.FieldByName(arySFields.Strings[intCount]).Value :=
      Self.adoTable.FieldByName(aryDFields.Strings[intCount]).Value;
    end;
    Self.sqlTable.Post;
    Self.adoTable.Next;
  end;
  Self.sqlConnection.CommitFreeAndNil(dbxTrans);
  arySFields.Free;
  aryDFields.Free;
  Self.adoTable.Close;
  Self.sqlTable.Close;


HOta  2014-09-30 19:13:30  No: 46714

Tableをあまり使ったことがないのです。
どうも、AppendでDataSetをNullで更新するようです。
OnNewRecordイベントでキーを設定するのではいかがでしょうか?

Tableではバックグラウンドで SQL 文を使っています。
早いうちにADOQueryを使ったほうがいいのではないでしょうか?
ADOQueryのInsert文を使えば問題ありません。
OracleがUpdate or Inset文をサポートしていれば便利です。


z  2014-10-01 04:27:39  No: 46715

お世話になります。

ご指摘頂いたようにTADOQueryやTSQLQueryを使えば動作することは
理解しているのですが、テーブルの型なども様々であり、コードが複雑に
なると実装時に判断して現状の仕様となりました。

ただ暫定対処はどうしても納得いかなかったので、
TSQLConnection→TSQLTable→TDataSetProvider→TClientDataSet を
TADConnection→TADTable に置き換える対応を行いました。
この対応でメモリエラーはおきなくなりました。
参考になるご意見色々ありがとうございました。


※返信する前に利用規約をご確認ください。

※Google reCAPTCHA認証からCloudflare Turnstile認証へ変更しました。






  このエントリーをはてなブックマークに追加