【问题标题】:Why is my stored procedure receiving a null parameter?为什么我的存储过程接收到一个空参数?
【发布时间】:2010-09-13 08:16:51
【问题描述】:

好的,这是一个卷发。我正在编写一些我没有编写的 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'

不幸的是,这对 lil ol' 我来说有点过分了。什么样的 TADOConnection 选项可能会对此产生影响?

有人有什么想法吗?

编辑:Update below(不想再问这个问题了:P)

【问题讨论】:

    标签: sql-server delphi stored-procedures


    【解决方案1】:

    警告:我不知道 delphi,但是这个问题敲响了一个微弱的钟声,所以我对它很感兴趣

    如果您使用 TADOStoredProc 而不是 TADOQuery,您会得到相同的结果吗? see delphi 5 developers guide

    另外,看起来第一个跟踪没有准备调用,并且认为@P1 是执行中的输出参数,而第二个跟踪执行了一个准备调用,@P1 作为输出,但没有将@P1 显示为输出在执行步骤中 - 这很重要吗?它确实看起来很奇怪,因此可能是一个线索

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

    祝你好运,请告诉我们你的发现!

    【讨论】:

      【解决方案2】:

      在我的程序中,我有 很多 代码与您的第一个 sn-p 非常相似,我还没有遇到过这个问题。

      那是实际上你的代码,还是你是如何表达问题让我们理解的? SQL 的文本是存储在 DFM 中还是动态填充?

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

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

      ParamByName("Foo").ParamType=ptInput;

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

      在 TSQLQuery 下,每当 SQL.Text 值更改时,查询的 Params 属性就会被重置/重新创建(我不确定 TADOQuery 是否如此),所以你的第一个 sn-p 应该已导致任何现有的 Params 信息被删除。

      如果上面的“ParamByname.ParamType”建议确实为您解决了问题,那么肯定在其他地方(在创建时?在表单上?)查询发生了一些事情,导致它认为 Foo 是一个输出参数。 ..

      这有帮助吗? :-)

      【讨论】:

        【解决方案3】:

        我怀疑您之前使用 ADOQuery 时遗留的一些参数不匹配。

        您是否尝试在更改 SQL.Text 后重新设置参数:

          ADOQuery1.Parameters.Refresh;
        

        您也可以尝试清除参数并明确地重新创建它们:

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

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

        【讨论】:

          【解决方案4】:
           ADOQuery1.Parameters.ParamByName("Foo").Value = Integer(someFunction()); 
          

          他们不使用:= 在 Object Pascal 中进行赋值吗?

          【讨论】:

          • 对不起,错字。我已经开始研究 c# 以及 Delphi,我现在经常犯这个错误:P
          【解决方案5】:

          @康斯坦丁

          这一定是问题作者的错字。

          @Blorgbeard

          嗯...当您更改 TADOQuery 的 SQL 时,可以很好地使用 清除参数并重新创建,然后使用 CreateParameter。 我不会在运行时依赖 ParamCheck - 因为它离开了 参数的属性大多未定义。 我在依赖 ParamCheck 时遇到过此类问题 自动填充参数 - 很少见但会发生。 啊,如果你走 CreateParameter 路线,首先创建 参数 @RETURN_VALUE 之一,因为它会捕获返回的 MSSQL SP 的值。

          【讨论】:

            【解决方案6】:

            我遇到过这样的问题的唯一一次是 DB Provider 无法区分 Output(始终将其设置为 null)和 InputOutput(使用您提供的)参数。

            【讨论】:

              【解决方案7】:

              好的,取得了进展……有点。

              @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
              除非现在有人有更聪明的想法..?

              无论如何,感谢大家迄今为止的建议。

              【讨论】:

                【解决方案8】:

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

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

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 2021-10-12
                  • 1970-01-01
                  • 2020-08-24
                  • 2014-01-14
                  • 2012-04-10
                  • 2011-04-22
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多