为什么我的存储过程接收到空参数?

时间:2020-03-06 14:45:48  来源:igfitidea点击:

好的,这是卷曲的。我正在处理一些我未编写的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我已通过调试器检查过的值。

但是,在我存储的proc中(出于调试目的而进行了更改):

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,该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选项可能会影响这一点?

有人有什么想法吗?

编辑:更新以下(不想再提这个问题了:P)

解决方案

警告:我不知道德尔福,但这个问题敲响了微弱的钟声,所以我对此感兴趣

如果使用TADOStoredProc而不是TADOQuery会得到相同的结果吗?请参见delphi 5开发人员指南

同样,看起来第一条迹线没有执行prepare调用,并认为@ P1是execute中的输出参数,而第二条迹线进行了以@ P1作为输出的prepare调用,但未在执行中显示@ P1作为输出这一步很重要吗?看起来确实很奇怪,所以可能是一个线索

我们也可以尝试将函数调用替换为常量1

祝我们好运,请告诉我们我们所发现的内容!

在我的程序中,我有很多代码与第一个代码段非常相似,并且我还没有遇到此问题。

那实际上是代码,还是我们如何表示问题以便我们理解?是将SQL文本存储在DFM中还是动态填充?

我想知道查询的Params属性是否已经在IDE中获得了已定义/缓存的参数列表,这可能解释了为什么P1被视为输出(几乎肯定会导致NULL问题)。

在设置ParamByName.Value之前,请尝试

ParamByName("Foo").ParamType=ptInput;

我不确定为什么更改连接字符串也可以解决此问题,除非它重置了该查询的参数内部含义。

在TSQLQuery下,每当更改SQL.Text值时,查询的Params属性都会被重置/重新创建(我不确定这对于TADOQuery是否正确),因此第一个代码段应该引起了任何现有的Params信息已被删除。

如果上面的" ParamByname.ParamType"建议确实为我们解决了问题,那么肯定在其他地方(在创建时?在窗体上?)上发生了查询查询,导致它认为Foo是输出参数...

这些帮助有用? :-)

我怀疑我们在先前使用ADOQuery时遗留了一些参数不匹配的情况。

我们是否尝试过在更改SQL.Text之后重设参数:

ADOQuery1.Parameters.Refresh;

我们也可以尝试清除参数并显式重新创建它们:

ADOQuery1.Parameters.Clear;
  ADOQuery1.Parameters.CreateParameter('Foo', ftInteger, pdInput, 0, 1);
  [...]

我认为更改连接实际上会强制使用参数的InternalRefresh。

ADOQuery1.Parameters.ParamByName("Foo").Value = Integer(someFunction());

他们不在对象Pascal中使用:=进行分配吗?

@康斯坦丁

它必须是问题作者的错字。

B

嗯...当我们更改TADOQuery的SQL时,很好用
清除参数并重新创建,然后使用CreateParameter。
我不会在运行时依赖ParamCheck,因为它离开了
参数的属性大多未定义。
依靠ParamCheck来解决此类问题
自动填充参数很少,但是会发生。
嗯,如果我们走CreateParameter路线,请先创建
参数@RETURN_VALUE一个,因为它将捕获返回的
MSSQL SP的值。

我唯一遇到过这样的问题是,数据库提供程序无法区分Output(始终将其设置为null)和InputOutput(使用我们提供的参数)参数时。

好的,进步了。

@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和$ F)2(DBPARAMFLAGS_ISOUTPUT),而新连接是1(DBPARAMFLAGS_ISINPUT)。

至少到目前为止,我不确定我是否想对此进行更深入的研究。

在获得更多时间和兴趣之前,我将确保在打开查询之前将所有参数都设置为" pdInput"。
除非有人现在有更多聪明的主意..?

无论如何,感谢大家到目前为止的建议。