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

时间:2008-09-26 03:05:03

标签: sql-server delphi stored-procedures

好的,这是卷曲的。我正在研究一些我没写过的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)

8 个答案:

答案 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)2DBPARAMFLAGS_ISOUTPUT),并且使用新连接时,1DBPARAMFLAGS_ISINPUT )。

我不确定我是否想深入挖掘这一点,至少目前为止。

在我有更多时间和倾向之前,我会确保在打开查询之前所有参数都设置为pdInput
除非现在有人有更好的想法..?

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

答案 7 :(得分:0)

使用TADOQuery检索一些LDAP信息时,我遇到了一个非常相似的问题,即使您的查询中没有参数,TParameter.InternalRefresh函数中的一个错误也会导致访问冲突。

要解决此问题,只需将TADOQuery.ParamCheck设置为false。