Joã*_*ira 4 delphi access-violation tadoquery
我有这个代码返回访问冲突('模块'sqloledb.dll'中地址74417E44的访问冲突'.读取地址786E3552')我无法确定问题出在哪里.我唯一的猜测是ADOQuery对我们可以传递的参数数量有限制.代码如下:
With qryInsert do
begin
Active := False;
Close;
Sql.Clear;
Sql.Add('Insert Into MyTable(ColumnOne, ');
Sql.Add(' ColumnTwo, ');
Sql.Add(' ColumnThree, ');
Sql.Add(' ColumnFour, ');
Sql.Add(' ColumnFive, ');
Sql.Add(' ColumnSix, ');
Sql.Add(' ColumnSeven, ');
Sql.Add(' ColumnEight, ');
Sql.Add(' ColumnNine, ');
Sql.Add(' ColumnTen, ');
Sql.Add(' ColumnEleven, ');
Sql.Add(' ColumnTwelve, ');
if qrySelect.FieldByName('ColumnTwelve').AsSTring = 'Y' then
begin
Sql.Add(' ColumnThirteen, ');
Sql.Add(' ColumnFourteen, ');
Sql.Add(' ColumnFifteen, ');
end;
Sql.Add(' ColumnSixteen, ');
if qrySelect.FieldByName('ColumnSixteen').AsSTring = 'Y' then
begin
Sql.Add(' ColumnSeventeen, ');
Sql.Add(' ColumnEighteen, ');
Sql.Add(' ColumnNineteen, ');
end;
if qrySelect.FieldByName('ColumnTwenty').AsSTring = 'Y' then
begin
Sql.Add(' ColumnTwenty, ');
Sql.Add(' ColumnTwentyOne, ');
Sql.Add(' ColumnTwentyTwo, ');
Sql.Add(' ColumnTwentyThree, ');
end
else
Sql.Add(' ColumnTwenty, ');
Sql.Add(' ColumnTwentyFour) ');
Sql.Add('Values(:ColumnOne, :ColumnTwo, :ColumnThree, :ColumnFour, ');
Sql.Add(' :ColumnFive, ' + dateDB + ', :ColumnSeven, ');
Sql.Add(' :ColumnEight, :ColumnNine, :ColumnTen, ');
Sql.Add(' :ColumnEleven, ');
Sql.Add(' :ColumnTwelve, ');
if qrySelect.FieldByName('ColumnTwelve').AsSTring = 'Y' then
Sql.Add(' :ColumnThirteen, :ColumnFourteen, :ColumnFifteen, ');
Sql.Add(' :ColumnSixteen, ');
if qrySelect.FieldByName('ColumnSixteen').AsSTring = 'Y' then
Sql.Add(' :ColumnSeventeen, :ColumnEighteen, :ColumnNineteen, ');
if qrySelect.FieldByName('ColumnTwenty').AsSTring = 'S' then
begin
Sql.Add(' :ColumnTwenty, ');
Sql.Add(' :ColumnTwentyOne, :ColumnTwentyTwo, :ColumnTwentyThree, ');
end
else
Sql.Add(' :ColumnTwenty, ');
Sql.Add(' :ColumnTwentyFour) ');
{And then for all the parameteres, pass the value}
Parameters.ParamByName('ColumnOne').Value := varColumnOne;
...
Parameters.ParamByName('ColumnTwentyFour').Value := varColumnTwentyFour;
ExecSQL;
end;
Run Code Online (Sandbox Code Playgroud)
我在这一行得到错误:
Sql.Add(' :ColumnTwelve, ');
Run Code Online (Sandbox Code Playgroud)
这是我的insert语句中的第11个参数.如果我评论这一行,我会在下一个参数中得到错误.如果我像这样直接输入值:
Sql.Add(' ' + varColumnTwelve + ', ');
Run Code Online (Sandbox Code Playgroud)
它工作正常,但我在下一个参数中得到错误.
所以它让我想知道:ADOQuery是否限制了它可以处理的参数数量?或者,如果这不是真正的问题,有没有人知道如何解决这个问题?
笔记:
我正在使用Delphi 7和Windows 8.1.
调试时只显示AV(并且始终显示),如果我通过".exe"直接执行应用程序,它永远不会出现.
如果我在出现错误后继续按"运行",它会显示越来越多的AV(我认为AV的数量与第10次之后添加的参数的数量相同),直到应用程序继续正常运行.
插件在屏幕上出现所有AV后工作.我只是想明白为什么一切看起来都很好我会收到这个错误.
更改TADOQuery的SQL属性会导致TADOQuery响应该更改,将修改后的SQL重新应用于内部ADO组件对象,以及重新解析SQL以识别任何参数.
因此,建议不要以这种方式逐步修改SQL.除了其他任何东西之外,在完全组装之前,一遍又一遍地应用和解析SQL是非常低效的.
在这种情况下,当您到达添加第11个参数时,SQL已经应用并解析了28次!
然后,结果发生在SQLOLEDB.DLL中的事实表明,无论发生什么问题,都会将SQL的更改应用于内部ADO对象,而不是在VCL处理中识别参数等.因此,没有太多你能够做到解决问题.你能做的最好的就是避免它.
您可以通过在修改SQL时设置ParamCheck:= FALSE来消除部分处理.这将阻止VCL尝试重新解析修改后的SQL以识别参数.但是,它不会阻止SQL重新应用于底层ADO组件以响应每个更改.
作为诊断练习,您可以尝试在修改SQL时设置ParamCheck:= FALSE.完成后,调用Parameters.Refresh方法以确保更新参数集合以反映已完成的SQL:
qryInsert.ParamCheck := FALSE;
qryInsert.SQL.Add(..);
qryInsert.SQL.Add(..);
qryInsert.SQL.Add(..);
qryInsert.SQL.Add(..);
qryInsert.Parameters.Refresh;
Run Code Online (Sandbox Code Playgroud)
注意: 将ParamCheck设置为FALSE时,必须在尝试设置任何参数值之前调用Parameters.Refresh,否则参数集合中将不存在参数!
如果在此更改之后AV仍然出现,则更强烈地表明内部ADO组件在响应SQL的重复更改时表现不佳的一些问题,可能是由于未能正确处理不完整(语法错误)的SQL.
但是,您可以通过两种方法之一完全避免触发更改机制.
也许最简单的方法是在构建SQL的代码周围的TADOQuery SQL 字符串列表中使用BeginUpdate/EndUpdate:
qryInsert.SQL.BeginUpdate;
try
qryInsert.SQL.Add(..);
qryInsert.SQL.Add(..);
qryInsert.SQL.Add(..);
finally
qryInsert.SQL.EndUpdate;
end;
Run Code Online (Sandbox Code Playgroud)
这样可以抑制ADO查询对象内部的OnChange事件,直到调用EndUpdate,此时SQL将应用于内部ADO对象并更新查询对象的Parameters.
或者,您可以将SQL组装在一个完全独立的字符串列表中,然后将其作为对SQL.Text属性的单个直接更改应用于TADOQuery SQL 属性:
sql := TStringList.Create;
try
sql.Add(..);
sql.Add(..);
sql.Add(..);
sql.Add(..);
sql.Add(..);
sql.Add(..);
qryInsert.SQL.Text := sql.Text;
finally
sql.Free;
end;
Run Code Online (Sandbox Code Playgroud)
无论哪种方式,结果将是VCL将解析参数,并且内部ADO对象将仅更新一次,具有完整且(希望)语法正确的SQL语句.
第二种方法可能涉及少一点"样板" - 尝试 .. 最后这里纯粹是为了管理临时字符串列表.如果你为了这个目的而在更广泛的范围内重用一个对象,或者使用一个产生一个简单字符串的SQL构建器助手类(就像我一样)那么就不需要这个特别的尝试了 ...... 最后,再多做一点申请方便,清洁:
SQLBuilder.Insert('MyTable');
SQLBuilder.AddColumn('ColumnOne');
SQLBuilder.AddColumn('ColumnTwo');
qryInsert.SQL.Text := SQLBuilder.SQL;
// qryInsert.SQL == INSERT INTO MyTable (ColumnOne, ColumnTwo)
// VALUES (:ColumnOne, :ColumnTwo)
Run Code Online (Sandbox Code Playgroud)
例如.
如果您构建SQL的首选技术产生一个字符串列表而不是一个简单的字符串,您可能会想直接分配字符串列表:
qryInsert.SQL := sql;
Run Code Online (Sandbox Code Playgroud)
但要注意,这个执行分配()中的SQL的StringList,有效地执行"深拷贝".您仍然需要确保已正确释放分配的字符串列表(上面代码中的sql).
还要注意,这也是效率较低的,因为它还复制了stringlist的其他属性,包括与列表中每个字符串相关联的任何对象.在这种情况下,您只对复制字符串列表的Text内容感兴趣,不需要产生(轻微)和不必要的开销.