好的,这是卷曲的。我正在研究一些我没写过的Delphi代码,而且我遇到了一个非常奇怪的问题。我的一个存储过程参数以null
形式出现,即使它已被发送1
。
Delphi代码使用TADOQuery来执行存储过程(匿名):
ADOQuery1.SQL.Text := "exec MyStoredProcedure :Foo,:Bar,:Baz,:Qux,:Smang,:Jimmy";
ADOQuery1.Parameters.ParamByName("Foo").Value := Integer(someFunction());
// other parameters all set similarly
ADOQuery1.ExecSQL;
Integer(SomeFunction())
目前始终返回1 - 我使用调试器进行了检查。
但是,在我的存储过程中(为了调试目的而改变):
create procedure MyStoredProcedure (
@Foo int, @Bar int, @Baz int,
@Qux int, @Smang int, @Jimmy varchar(20)
) as begin
-- temp debug
if ( @Foo is null ) begin
insert into TempLog values ( "oh crap" )
end
-- do the rest of the stuff here..
end
TempLog
确实最终得到了“哦垃圾”(旁边的问题:必须有更好的方法来调试存储过程:它是什么?)。
以下是分析器的示例跟踪:
exec [MYDB]..sp_procedure_params_rowset N'MyStoredProcedure',1,NULL,NULL
declare @p3 int
set @p3=NULL
exec sp_executesql
N'exec MyStoredProcedure @P1,@P2,@P3,@P4,@P5,@P6',
N'@P1 int OUTPUT,@P2 int,@P3 int,@P4 int,@P5 int,@P6 int',
@p3 output,1,1,1,0,200
select @p3
这对我来说有点奇怪。请注意,它正在使用@ p3 和 @ P3 - 这是否会导致我的问题?
另一个奇怪的是它似乎取决于我使用的是哪个TADOConnection。
该项目是一个dll,它从另一个应用程序传递一个TADOConnection。它使用此连接调用所有存储过程。
如果不使用此连接,我首先执行此操作:
ConnectionNew := TADOQuery.Create(ConnectionOld.Owner);
ConnectionNew.ConnectionString := ConnectionOld.ConnectionString;
TADOQuery1.Connection := ConnectionNew;
然后问题不会发生!这种情况的痕迹是:
exec [MYDB]..sp_procedure_params_rowset N'MyStoredProcedure',1,NULL,NULL
declare @p1 int
set @p1=64
exec sp_prepare @p1 output,
N'@P1 int,@P2 int,@P3 int,@P4 int,@P5 int,@P6 varchar(20)',
N'exec MyStoredProcedure @P1,@P2,@P3,@P4,@P5,@P6',
1
select @p1
SET FMTONLY ON exec sp_execute 64,0,0,0,0,0,' ' SET FMTONLY OFF
exec sp_unprepare 64
SET NO_BROWSETABLE OFF
exec sp_executesql
N'exec MyStoredProcedure @P1,@P2,@P3,@P4,@P5,@P6',
N'@P1 int,@P2 int,@P3 int,@P4 int,@P5 int,@P6 varchar(20)',
1,1,1,3,0,'400.00'
不幸的是,对于我来说,这有点儿了。什么样的TADOConnection选项可能会影响这个?
有没有人有任何想法?
修改: Update below(不想再提出这个问题:P)
答案 0 :(得分:1)
在我的程序中,我的很多代码与您的第一个代码段非常相似,我没有遇到过这个问题。
实际您的代码,或者您是如何代表问题让我们理解的? SQL的文本是存储在DFM中还是动态填充的?
我想知道查询的Params属性是否已经在IDE中获得了定义/缓存的参数列表,这可能解释了为什么P1被视为输出(这几乎肯定会导致您的NULL问题) )。
在您设置ParamByName.Value之前,请尝试
ParamByName("Foo").ParamType=ptInput;
我不确定为什么更改连接字符串也会修复此问题,除非它重置了该查询的参数的内部意义。
在TSQLQuery下,只要SQL.Text值发生变化,我就会重置/重新创建查询的Params属性(我不确定TADOQuery是否适合你),这样你的第一个片段就应该引起任何现有的Params信息都已被删除。
如果上面的'ParamByname.ParamType'建议确实为你解决了这个问题,那么肯定会在其他地方(在表单上的创建时?)查询发生的事情导致它认为Foo是一个输出参数。 ..
这有帮助吗? : - )
答案 1 :(得分:0)
如果使用TADOStoredProc而不是TADOQuery,你会得到相同的结果吗? see delphi 5 developers guide
另外,看起来第一个跟踪没有准备调用,并认为@ P1是执行中的输出参数,而第二个跟踪执行使用@ P1作为输出的准备调用,但不显示@ P1作为输出在执行步骤中 - 这是重要的吗?它似乎很奇怪,所以可能是一个线索
您也可以尝试用常量1
替换函数调用 祝你好运,请让我们知道你发现了什么!答案 2 :(得分:0)
我怀疑您之前使用ADOQuery时遗留了一些参数不匹配。
您是否尝试在更改SQL.Text后重置参数:
ADOQuery1.Parameters.Refresh;
您也可以尝试清除参数并明确重新创建它们:
ADOQuery1.Parameters.Clear;
ADOQuery1.Parameters.CreateParameter('Foo', ftInteger, pdInput, 0, 1);
[...]
我认为更改连接实际上会强制参数的InternalRefresh。
答案 3 :(得分:0)
ADOQuery1.Parameters.ParamByName("Foo").Value = Integer(someFunction());
他们不是在Object Pascal中使用:=
进行分配吗?
答案 4 :(得分:0)
@Constantin
这一定是来自问题作者的拼写错误。
@Blorgbeard
嗯...当你改变TADOQuery的SQL时,很好用 清除参数并使用CreateParameter重新创建。 我不会在运行时依赖ParamCheck - 因为它离开了 参数的属性大多未定义。 我依靠ParamCheck来解决这类问题 自动填充参数 - 很少见但却发生。 啊,如果你去CreateParameter路线,先创建 参数@RETURN_VALUE,因为它会捕获返回的 MSSQL SP的值。
答案 5 :(得分:0)
我遇到这样的问题的唯一一次是DB Provider无法区分Output(始终将其设置为null)和InputOutput(使用您提供的)参数。
答案 6 :(得分:0)
好的,取得了进展......等等。
@Robsoft是正确的,将参数方向设置为pdInput
解决了问题。
我追溯到VCL代码,它归结为TParameters.InternalRefresh.RefreshFromOleDB
。在设置SQL.Text时调用此函数。这是(删节)代码:
function TParameters.InternalRefresh: Boolean;
procedure RefreshFromOleDB;
// ..
if OLEDBParameters.GetParameterInfo(ParamCount, PDBPARAMINFO(ParamInfo), @NamesBuffer) = S_OK then
for I := 0 to ParamCount - 1 do
with ParamInfo[I] do
begin
// ..
Direction := dwFlags and $F; // here's where the wrong value comes from
// ..
end;
// ..
end;
// ..
end;
因此,OLEDBParameters.GetParameterInfo
由于某种原因返回了错误的标志。
我已验证使用原始连接,(dwFlags and $F)
为2
(DBPARAMFLAGS_ISOUTPUT
),并且使用新连接时,1
(DBPARAMFLAGS_ISINPUT
)。
我不确定我是否想深入挖掘这一点,至少目前为止。
在我有更多时间和倾向之前,我会确保在打开查询之前所有参数都设置为pdInput
。
除非现在有人有更好的想法..?
无论如何,感谢大家到目前为止的建议。
答案 7 :(得分:0)
使用TADOQuery检索一些LDAP信息时,我遇到了一个非常相似的问题,即使您的查询中没有参数,TParameter.InternalRefresh函数中的一个错误也会导致访问冲突。
要解决此问题,只需将TADOQuery.ParamCheck设置为false。