【问题标题】:TADOStoredProc and SQL Server stored procedure with parameter as default valueTADStoredProc 和 SQL Server 存储过程,参数为默认值
【发布时间】:2017-11-08 15:27:29
【问题描述】:

我正在使用 Delphi 7 和 SQL Server 2008。我创建了一个虚拟表和虚拟存储过程,如下所示。

CREATE TABLE [dbo].[Persons]
(
    [P_ID] [int] IDENTITY(1,1) NOT NULL,
    [LastName] [varchar](255) NOT NULL
)

CREATE PROCEDURE [dbo].[p_dummy_proc]
    @p_id int,
    @p_name VARCHAR(10) = 'dummy' -- donot pass anything from delphi and it should take 'dummy'
AS
BEGIN 
    IF (@p_name is null)
        RAISERROR 123456 'why are you null'
    ELSE  
        INSERT INTO dbo.persons(LastName) 
        VALUES(@p_name)
END

我从 Delphi 调用上述过程是这样的:

procedure TForm1.Button1Click(Sender: TObject);
begin
  try
    ADOStoredProc1.Parameters.ParamByName('@p_id').Value := 10;
    ADOStoredProc1.ExecProc; // error why are you null
  except
   on E: EDatabaseError do
     ShowMessage(e.Message);
  end;
end;

在 Delphi 代码中,我没有传递第二个参数,我希望 SQL Server 应该采用它的默认值。

当我执行存储过程时,我得到了一个错误。为什么当我没有从 Delphi 传递任何东西时,SQL Server 不采用默认值?

如果我将参数值设置为“dummy”,它会按预期工作。

【问题讨论】:

  • 你得到的确切错误是什么?
  • @kirchner :更新了错误的问题。错误是“你为什么为空”。
  • @nil :: 我没有看到参数的任何绑定属性。
  • 好的,所以 SQL Server 正在接收一个明确的 NULL。为了应用默认值,您不能为参数传递任何内容,甚至不能传递 NULL。当您没有为代码中的参数提供值时,基础设施可能会传递 NULL 默认值?您可以尝试从参数列表中删除 @p_name 吗?
  • 如果我在设计时添加参数,我只能重现您描述的行为。如果我在设计时不这样做并在分配参数值之前在执行时调用Parameters.Refresh,则它可以正常工作 - 没有错误并且在分析器中调用会显示默认关键字。

标签: sql-server delphi delphi-7 ado


【解决方案1】:

TL;DR - 使用默认 SP 参数值集:

ADOStoredProc1.Parameters.ParamByName('@p_name').ParameterObject.Value := Unassigned

在 Delphi 代码中,我没有传递第二个参数,我是 期望 SQL Server 采用它的默认值。

是的,你是。您在设计时使用了一个已经存在的参数,因此该参数作为 NULL 显式发送到 SQL Server。您可以通过检查 SQL 分析器来验证这一点。

如果您必须使用设计时参数,您可以在运行时删除它,只要 default 参数打算使用或创建/分配它。例如(你可以为此制定一个通用方法):

var
  Param: TParameter;
  ParamName: WideString;
  ParamValue: Variant;
  UseDefaultParameter: Boolean;
begin
  ParamName := '@p_name';
  Param := ADOStoredProc1.Parameters.FindParam(ParamName);
  UseDefaultParameter := True; // or False to assign a value

  if UseDefaultParameter then
  begin
    if Assigned(Param) then
      ADOStoredProc1.Parameters.Delete(Param.Index);
  end
  else
  begin
    ParamValue := 'boo!';
    if Assigned(Param) then
      Param.Value := ParamValue
    else
      ADOStoredProc1.Parameters.CreateParameter(ParamName, ftString, pdInput, 10, ParamValue);
  end;
end;

否则,不要使用设计时参数并根据需要在运行时创建参数。 IMO 更好的方法是使用本地 TADOStoredProc,在运行时创建它,分配参数,执行它,然后销毁它。

就我个人而言,我在运行时创建我的TADOStoredProc 并调用Parameters.Refresh() 方法,该方法将从SQL Server 查询(并创建)参数。然后我只需将值分配给我需要的参数。虽然SQL Server多走一趟,但是在给SP添加新参数的时候维护起来还是很方便的。
如果未设置参数值,ADO 将启动exec 命令,使用default 关键字设置参数。即

exec p_dummy_proc @p_id=1, @p_name=default

进一步挖掘后,我注意到问题实际上出在TParameter.SetValue setter 中。没有办法“清除”参数值:

procedure TParameter.SetValue(const Value: Variant);
begin
  if VarIsEmpty(Value) or VarIsNull(Value) then <--
    NewValue := Null
  else
  begin
    ...
  end;  
  ParameterObject.Value := NewValue;
end;

您可以看到,如果您将Value 设置为NULLUnassignedParameterObject.Value 将设置为NULL(但绝不会设置为默认值)。

所以如果需要清除某个参数,并使用默认值,则需要直接将TParameter.ParameterObject.Value设置为Unassigned

if UseDefaultParameter then 
  SP.Parameters.ParamByName('@p_name').ParameterObject.Value := Unassigned
else
  SP.Parameters.ParamByName('@p_name').Value := 'boo!'

【讨论】:

  • 你最后的编辑很整洁。 ParameterObject.Value := Unassigned 基本上相当于我在调查这个问题时为 .NET 找到的 Parameter.Value = Nothing。但是我在 Delphi 中没有成功,因为我尝试使用 TParameter
  • @nil,我在调用Parameters.Refresh并检查参数后意识到,包括ParameterObject。我知道那里必须有可以“清除”参数值的东西。
  • 真的很喜欢Unassigned 的值,以前从未使用过,但从现在开始我会这样做
猜你喜欢
  • 2015-11-12
  • 2011-11-02
  • 2010-11-07
  • 2012-08-06
  • 1970-01-01
  • 2018-12-06
  • 1970-01-01
  • 2018-04-06
相关资源
最近更新 更多