ADO 查询导致 mdb 数据库超过 2GB 限制

Posted

技术标签:

【中文标题】ADO 查询导致 mdb 数据库超过 2GB 限制【英文标题】:ADO query causing mdb database to exceed 2GB limit 【发布时间】:2016-02-02 10:28:41 【问题描述】:

我正在使用下面的 Delphi 代码将 Access 数据库中的表替换为 utf=8 格式的大 csv 文件。在我开始之前,数据库是 946MB。一切正常,直到我的表达到一定大小然后我得到一个错误。发生这种情况时,数据库已超过 2GB。我猜也许 Jet 引擎正在创建一个导致溢出的内部副本 - 有没有办法阻止这种情况,或者也许有人可以建议一种不同的方法来导入我的数据来避免它?

function TCSVDatabase.ADOFromCSV(ConStr: string): Boolean;
var
  J, K: Integer;
  S: string;
  SN, DN: string;
  DefDir: string;
  DestDir: string;
  TN: TStringList;
begin
  BeginUpdate;
  Result := False;
  DoProgress(0, 'Initializing...');
  DN := ADOSetConnectionString(ConStr);
  if FileExists(DN) then
  begin
    DestDir := ExtractFilePath(DN);
    TN := TStringList.Create;
    Result := True;
    try
      DefDir := FDefaultPath;
      FDefaultPath := DestDir;
      TN.Assign(ADOGetTableNames);
      for K := 0 to TN.Count - 1 do
      begin
        SN := DestDir + TN[K] + '.csv';
        if FileExists(SN) then
        begin
          DoProgress(0, 'Opening "' + TN[K] + '"...');
          FADOTable.Close;
          while FADOTable.Active do;
          FADOTable.Connection := FADOConnection;
          FADOTable.TableName := TN[K];
          FADOTable.Open;
          if FADOTable.Active then
          begin
            Result := True;
            ADOGetFieldNames;
            FADOQuery.Connection := FADOConnection;
            FADOQuery.Recordset := FADOTable.Recordset;
            FADOQuery.Open;
            DoProgress(0, 'Emptying "' + TN[K] + '"...');
            FADOQuery.SQL.Text := 'DELETE * FROM [' + TN[K] + ']';
            FADOQuery.ExecSQL;
            S := ExtractFilePath(SN);
            SetLength(S, Length(S) - 1);
            try
              DoProgress(0, 'Filling "' + TN[K] + '"...');
              FADOQuery.SQL.Text := 'INSERT INTO [' + TN[K] +
                '] SELECT * FROM [' + ExtractFileName(SN) + '] IN "' + S +
                '" "Text;HDR=YES;FMT=Delimited(,);CharacterSet=65001;"';
              FADOQuery.ExecSQL;
            except
              FADOQuery.Connection := nil;
              FADOQuery.Close;
              DoProgress(0, 'SQL error in "' + TN[K] + '"');
$IFDEF VCL
              ShowMessage('SQL error in "' + TN[K]);
$ENDIF
              Result := False;
              Exit;
            end;
            FADOQuery.Connection := nil;
            FADOQuery.Close;
            for J := 0 to FTemp.Count - 1 do
            begin
              if (FTemp.Codes[J] in [FT_MEMO, FT_WIDEMEMO]) then
              begin
                FADOTable.First;
                while (not FADOTable.EOF) do
                begin
                  S := FADOTable.Fields[J].AsString;
                  if (Pos('_\', S) <> 0) then
                  begin
                    S := StringReplace(S, '_\r\n_', #13#10, [rfReplaceAll]);
                    S := StringReplace(S, '_\r_', #13, [rfReplaceAll]);
                    S := StringReplace(S, '_\n_', #10, [rfReplaceAll]);
                    FADOTable.Edit;
                    FADOTable.Fields[J].AsString := S;
                    FADOTable.Post;
                  end;
                  FADOTable.Next;
                end;
              end;
            end;
          end;
        end;
        DoProgress(0, '');
      end;
    finally
      FADOTable.Close;
      if Result then
      begin
        DoProgress(0, 'Compacting...');
        CompactDatabase(DN, FADOConnection);
      end;
      FDefaultPath := DefDir;
      TN.Free;
    end;
  end;
  DoProgress(0, '');
  EndUpdate;
end;

【问题讨论】:

什么版本的 Access? 切换到另一个数据库,你已经接近极限了。看看使用 ZeosLib 作为替代方案的 SQLite 您的问题没有说明您从 CSV 文件中读取的数据量。仅当 CSV 文件的总大小加上原始 946 MB (远)小于 2 GB 时,您对内部副本的全部猜测才有意义。否则,您只会遇到其他人回答的 2GB 限制。请edit 并添加相关信息,以便我们区分我们正在处理的两种情况中的哪一种。 正确,Jan - 我正在尝试添加大约 40MB,所以当它出错时它几乎是 2GB 限制的一半。 考虑在删除语句之后执行数据库压缩。如果您要截断整个表,您可以通过先压缩来释放 mdb 文件中的空间。 【参考方案1】:

我的解决方案是清除表并在两个单独的通道中重新填充它们,中间有一个 CompactDatabase。感谢大家的建议,我希望我的解决方案对其他人有帮助:-)

PS 谢谢山姆,我在测试我的修复后才看到你的答案,基本上就是你的建议!

function TCSVDatabase.ADOFromCSV(ConStr: string): Boolean;
var
  Err: Cardinal;
  J, K: Integer;
  S: string;
  SN, DN: string;
  DefDir: string;
  DestDir: string;
  TN: TStringList;
begin
  BeginUpdate;
  Result := False;
  DoProgress(0, 'Initializing...');
  DN := ADOSetConnectionString(ConStr);
  if FileExists(DN) then
  begin
    DestDir := ExtractFilePath(DN);
    TN := TStringList.Create;
    Result := True;
    try
      DefDir := FDefaultPath;
      FDefaultPath := DestDir;
      TN.Assign(ADOGetTableNames);
      for K := 0 to TN.Count - 1 do
      begin
        SN := DestDir + TN[K] + '.csv';
        if FileExists(SN) then
        begin
          DoProgress(0, 'Emptying "' + TN[K] + '"...');
          FADOTable.Close;
          while FADOTable.Active do;
          FADOTable.Connection := FADOConnection;
          FADOTable.TableName := TN[K];
          FADOTable.Open;
          if FADOTable.Active then
          begin
            Result := True;
            ADOGetFieldNames;
            FADOQuery.Connection := FADOConnection;
            FADOQuery.Recordset := FADOTable.Recordset;
            FADOQuery.Open;
            FADOQuery.SQL.Text := 'DELETE * FROM [' + TN[K] + ']';
            FADOQuery.ExecSQL;
          end;
        end;
        DoProgress(0, '');
      end;
      DoProgress(0, 'Compacting...');
      CompactDatabase(DN, FADOConnection);
      DN := ADOSetConnectionString(ConStr);
      for K := 0 to TN.Count - 1 do
      begin
        SN := DestDir + TN[K] + '.csv';
        if FileExists(SN) then
        begin
          DoProgress(0, 'Opening "' + TN[K] + '"...');
          FADOTable.Close;
          while FADOTable.Active do;
          FADOTable.Connection := FADOConnection;
          FADOTable.TableName := TN[K];
          FADOTable.Open;
          if FADOTable.Active then
          begin
            Result := True;
            ADOGetFieldNames;
            FADOQuery.Connection := FADOConnection;
            FADOQuery.Recordset := FADOTable.Recordset;
            FADOQuery.Open;
            S := ExtractFilePath(SN);
            SetLength(S, Length(S) - 1);
            try
              DoProgress(0, 'Filling "' + TN[K] + '"...');
              FADOQuery.SQL.Text := 'INSERT INTO [' + TN[K] +
                '] SELECT * FROM [' + ExtractFileName(SN) + '] IN "' + S +
                '" "Text;HDR=YES;FMT=Delimited(,);CharacterSet=65001;"';
              FADOQuery.ExecSQL;
            except
              FADOQuery.Connection := nil;
              FADOQuery.Close;
              DoProgress(0, 'Error in "' + TN[K] + '"');
              ShowMessage('Error in "' + TN[K] + '"' );
              Result := False;
              Exit;
            end;
            FADOQuery.Connection := nil;
            FADOQuery.Close;
            for J := 0 to FTemp.Count - 1 do
            begin
              if (FTemp.Codes[J] in [FT_MEMO, FT_WIDEMEMO]) then
              begin
                FADOTable.First;
                while (not FADOTable.EOF) do
                begin
                  S := FADOTable.Fields[J].AsString;
                  if (Pos('_\', S) <> 0) then
                  begin
                    S := StringReplace(S, '_\r\n_', #13#10, [rfReplaceAll]);
                    S := StringReplace(S, '_\r_', #13, [rfReplaceAll]);
                    S := StringReplace(S, '_\n_', #10, [rfReplaceAll]);
                    FADOTable.Edit;
                    FADOTable.Fields[J].AsString := S;
                    FADOTable.Post;
                  end;
                  FADOTable.Next;
                end;
              end;
            end;
          end;
        end;
        DoProgress(0, '');
      end;
    finally
      FADOTable.Close;
      if Result then
      begin
        DoProgress(0, 'Compacting...');
        CompactDatabase(DN, FADOConnection);
      end;
      FDefaultPath := DefDir;
      TN.Free;
    end;
  end;
  DoProgress(0, '');
  EndUpdate;
end;

【讨论】:

【参考方案2】:

您需要拆分数据库。来自Access 2007 specifications

Access 数据库 (.accdb) 文件大小

2 GB,减去系统对象所需的空间

注意虽然单个数据库文件的最大大小为 2GB,但 您可以通过使用拆分数据库来解决此限制。一种 前端数据库文件可以指向数千个后端数据库 文件,每个文件可以大到 2GB。了解更多信息, 请参阅主题拆分 Access 数据库。

您还应该考虑迁移到另一个关系数据库管理系统 (RDBMS),例如 Oracle、SQL 服务器、FireBird(您自己命名)

【讨论】:

数据库为 946 MB,我尝试添加的数据约为 40 MB。所以它总共几乎正好是 1GB,这就是为什么我猜测当我运行查询时某些东西的大小会暂时翻倍。数据库是大型组织的财产,我无法更改格式。 @rshotbolt:所以你是说我不在乎你写的是什么答案,只要是我想听到的。不要给我事实或文件。 多么荒谬的评论,我当然对所有建议都感兴趣。该数据库是为自然历史博物馆物种搜索提供动力的英国物种清单 (UKSI),以及国家生物多样性网络在线记录应用程序,例如iRecord,我正在与他们的数据库管理员一起尝试解决这个问题。更改为新数据库不是一种选择。

以上是关于ADO 查询导致 mdb 数据库超过 2GB 限制的主要内容,如果未能解决你的问题,请参考以下文章

使用 ADO 将大型 csv 文件导入 mdb 时的性能问题

c#ADO操作Access的mdb数据库只能读不能修改的解决方法

奇怪的情况,使用 ADO 在 Excel VBA 中定义连接到 Access .accdb 数据库的文件路径,但是它告诉我找不到 .mdb 文件

如何为 Windows 中的进程分配超过 2GB 的内存?

如何将超过 2Gb 的文件上传到 IIS 7.5 / .Net 4?

MySql 查询数据时间过长,导致程序报错 怎么解决