为一条记录打开 TFDQuery 时的 EOutOfMemory

Posted

技术标签:

【中文标题】为一条记录打开 TFDQuery 时的 EOutOfMemory【英文标题】:EOutOfMemory when open TFDQuery for one record 【发布时间】:2019-12-13 14:05:23 【问题描述】:

    当我尝试从表中获取一条记录时,出现错误。我用TFDQuery (select SLDDATA, SLDTIME from TSYSLOGDATA WHERE SLDTIME = '2019-12-11 00:00:00.000')

    异常类:EOutOfMemory 主线程(6a28 美元): 00bfaeda +066 TDAdmin.exe FireDAC.Phys.ODBCWrapper 3752 +13 TODBCVariable.AllocLongData 00bff3f8 +1a8 TDAdmin.exe FireDAC.Phys.ODBCWrapper 5178 +51 TODBCStatementBase.GetLongVar 00bff52b +06b TDAdmin.exe FireDAC.Phys.ODBCWrapper 5225 +10 TODBCStatementBase.FetchLateBindedColumns 00bff63b +0ff TDAdmin.exe FireDAC.Phys.ODBCWrapper 5266 +29 TODBCStatementBase.Fetch 00c09ffa +05a TDAdmin.exe FireDAC.Phys.ODBCBase 2996 +7 TFDPhysODBCCommand.InternalFetchRowSet 00be8742 +066 TDAdmin.exe FireDAC.Phys 8669 +8 DoFetch

数据库位于 MS SQL Server 上。该表有两列 SldDate DateTime 和 SldData Nvarchar(max)。没有主键。 SldData 包含 422971068 字节的字符串。正如我调查的那样,这个长字符串是问题的一个原因。

有什么办法解决这个问题吗?

    对于 211485518 字节的字符串,我遇到了同样的错误。

    我尝试使用参数时遇到访问冲突

    FDQuery1.SQL.Text := 'SELECT :SYSLOGDATA = SLDDATA FROM TSYSLOGDATA WHERE SLDTIME = ''2019-12-15 00:00:00.000'''; 使用 FDQuery1.Params[0] 开始 数据类型:= ftWideMemo; 参数类型:= ptOutput; 结尾; FDQuery1.Command.CommandKind := skExecute; FDQuery1.ExecSQL;

Project Project1.exe 引发异常类 $C0000005,并带有消息“0x004075bf 处的访问冲突:写入地址 0x6b3b0000”。

System.Move(???,???,???)
:004075bf Move + $77
FireDAC.Phys.ODBCBase.ProcessArrayItem(???,???,0,???)
FireDAC.Phys.ODBCBase.TFDPhysODBCCommand.GetParamValues(???,0,-1)
FireDAC.Phys.ODBCBase.TFDPhysODBCCommand.InternalExecute(1,0,???)
FireDAC.Phys.Process_HandleSystemFailure(1,0,-1,False)
FireDAC.Phys.Process_SingleRow
FireDAC.Phys.TFDPhysCommand.ExecuteBase(1,0)
FireDAC.Phys.TFDPhysCommandAsyncExecute.Execute
FireDAC.Stan.Async.TFDStanAsyncExecutor.ExecuteOperation(False)
FireDAC.Stan.Async.TFDStanAsyncExecutor.Run FireDAC.Phys.TFDPhysCommand.ExecuteTask(TFDPhysCommandAsyncExecute($2A8BDF8) as IFDStanAsyncOperation,TFDCommand($29904D0) as IFDStanAsyncHandler,False)
FireDAC.Phys.TFDPhysCommand.Execute(???,???,False)
FireDAC.Comp.Client.TFDCustomCommand.InternalExecute(0,0,False)
FireDAC.Comp.Client.TFDCustomCommand.Execute(0,0,False)
FireDAC.Comp.Client.TFDAdaptedDataSet.DoExecuteSource(0,0)
FireDAC.Comp.DataSet.TFDDataSet.Execute(0,0)
FireDAC.Comp.Client.TFDCustomQuery.ExecSQL
    我试图像这样按块获取数据。我运行第一个 sql 脚本。获取数据并放入局部变量。当我尝试从字段 (FDQuery1.Fields[1].AsString) 获取数据时,我在第二个 sql 脚本上遇到了相同的错误 EOutOfMemory

SELECT SUBSTRING(SLDDATA, 1, 100000000) SLDDATA, SLDTIME 从 TSYSLOGDATA SELECT SUBSTRING(SLDDATA, 100000001, 200000000) SLDDATA, SLDTIME 从 TSYSLOGDATA

【问题讨论】:

如果您尝试一次处理所有 422MB 数据块,将会导致 32 位软件出现问题。需要在分解/蒸煮或使用 64 位软件之间进行选择。 可能涉及到多个缓冲区,每个缓冲区都保存着这些数据的副本。即使有足够的空闲内存,也可能一个块都用不完。 @Brian 422MB 对于 32 位软件来说没问题。 422GB 是。 @UweRaabe 需要多份 422MB 缓冲区的副本才能耗尽可用内存 @RemyLebeau,鉴于在 32 位中我们通常有 2GB 可用内存,这将是大约 5 个该缓冲区的副本要填充 - 假设程序不使用任何其他内存并且碎片允许将每个分配为一个块。 【参考方案1】:

您应该尝试使用 Blob Streaming: http://docwiki.embarcadero.com/RADStudio/Tokyo/en/Support_for_Blob_Streaming_in_FireDAC 从上面引用:

与“按值”相比,客户端内存使用量最小化 额外内存使用的要求等于 BLOB 的 3-4 倍 值大小;

【讨论】:

你的意思是我必须将 SQL Server FILESTREAM 与流一起使用吗?我找不到如何将数据从字段加载到流。我试过这个,但它不起作用。 FDQuery1.SQL.Text := 'SELECT :Res = SlDData FROM TSYSLOGDATA WHERE SLDTIME = ''2019-12-15 00:00:00.000'' '; FDQuery1.Params[0].DataType := ftStream; FDQuery1.Params[0].ParamType := ptOutput; FDQuery1.Params[0].StreamMode := smOpenReadWrite; FDQuery1.OpenOrExecute; FDQuery1.Params[0].AsStream.Read(Buffer, Length(Buffer));

以上是关于为一条记录打开 TFDQuery 时的 EOutOfMemory的主要内容,如果未能解决你的问题,请参考以下文章

如何使用行号将多条记录合并为一条?

将 2 个“一对多”表中的记录转置为一条记录

如何使用findBy ..()方法为一条记录编写查询

如何在Oracle 10g中根据指定列将n条记录合并为一条记录? [复制]

关于Matlab小波工具箱打开.mat文件为一条递增斜线问题的解决办法

多用户操作一个数据表时的并发性操作