【问题标题】:How to pass null to SQL Server stored procedure param with NON-NULL default value如何使用 NON-NULL 默认值将 null 传递给 SQL Server 存储过程参数
【发布时间】:2019-02-06 23:28:30
【问题描述】:

我在多租户数据库中有一个 SQL Server 存储过程。它被所有客户端使用,但由于我不会同时更新它们,所以我需要提供默认参数值,这样客户端如果没有传入参数就不会抛出错误。

看起来像这样:

ALTER PROCEDURE [dbo].[sp_UpdateSearchValues]  
    @pAuthID NVARCHAR(255),  
    @pgKey INT,   
    @pSearch01 NVARCHAR(255) = 'BTR:NOSEARCHUPDATE',  
    @pSearch02 NVARCHAR(255) = 'BTR:NOSEARCHUPDATE',  
    ..  
    @pSearch30 NVARCHAR(255) = 'BTR:NOSEARCHUPDATE'  

我不能使用null 作为默认值,因为null 是一个可以传入的有效值(以清除搜索索引)。但是,当我将参数分配给DBNull.Value 时,似乎存储过程认为什么都没有传递,然后存储过程在其逻辑中使用默认的“BTR:NOSEARCHUPDATE”并且什么都不做(而是清除值)- 如果我分配 DBNull,则下面的 != 'BTR;NOSEARCHUPDATE' 评估为 false。

CASE WHEN @pSearch01 != 'BTR:NOSEARCHUPDATE' THEN @pSearch01 ELSE pSearch01 END,

此语句只是尝试获取参数的值,如果“传入了某些内容(DBNull 或值)”,否则回退到相应 db 字段中的现有值。

我使用以下代码分配DBNull

UpdateCommand.Parameters[ "@pSearch19" ].Value = DBNull.Value;

所以问题是,如何将“null”传递给存储过程,以便它使用它作为值,而不是简单地使用默认值“BTR:NOSEARCHUPDATE”?

【问题讨论】:

  • ado.net 将在传递 vb.net 'Nothing' 或 c# 'Null' 对象时使用默认值。 DBNull.Value 应该传入一个实际的 DBNull(在@David Browne 的回答中突出显示。在执行语句之前,您的代码中是否发生了其他可能会改变参数值的事情?
  • 旁注:您应该为您的存储过程使用sp_ 前缀。微软有reserved that prefix for its own use (see Naming Stored Procedures),你确实会在未来某个时候冒着名称冲突的风险。 It's also bad for your stored procedure performance。最好只是简单地避免 sp_ 并使用其他东西作为前缀 - 或者根本不使用前缀!
  • @marc_s 是的,我知道 sp_ 前缀。但是代码是 15 年前(错误地)编写的,我无法更改这些 SP 名称。 :(
  • 你能用空字符串代替NULL吗?

标签: c# sql sql-server sqlcommand default-parameters


【解决方案1】:

null 是要传递的有效值,它不会被默认参数值覆盖。我无法在 T-SQL 或 ADO.NET 中重现此行为。

EG 为

create or alter procedure [dbo].[sp_UpdateSearchValues]  
    @pAuthID nvarchar(255),  
    @pgKey int,   
    @pSearch01 nvarchar(255) = 'BTR:NOSEARCHUPDATE',  
    @pSearch02 nvarchar(255) = 'BTR:NOSEARCHUPDATE',  
    @pSearch30 nvarchar(255) = 'BTR:NOSEARCHUPDATE' 
as
begin
    select @pSearch01 search01
end

然后在 .NET 中

using (var con = new SqlConnection("server=.;database=tempdb;integrated security=true"))
{
   con.Open();

   SqlCommand cmd = con.CreateCommand();
   cmd.CommandText = "sp_UpdateSearchValues";
   cmd.CommandType = System.Data.CommandType.StoredProcedure;

   var pAuthID = cmd.Parameters.Add("@pAuthID", SqlDbType.NVarChar,255 );
   var pgKey = cmd.Parameters.Add("@pgKey", SqlDbType.Int);
   var pSearch01 = cmd.Parameters.Add("@pSearch01", SqlDbType.NVarChar, 255);

   pAuthID.Value = "a";
   pgKey.Value = 1;
   pSearch01.Value = DBNull.Value;

   var r = cmd.ExecuteScalar();

   Console.WriteLine($"{r} {r.GetType().Name}");

   Console.ReadKey();
}

输出

DBNull

但看起来这是一个空比较和三值逻辑的简单案例。

考虑:

declare @pSearch01 nvarchar(200) = null
select CASE WHEN @pSearch01 != 'BTR:NOSEARCHUPDATE' then 1 else 0 end

返回什么? null != 'BTR:NOSEARCHUPDATE' 是真的陈述吗?不,不是。

【讨论】:

  • 我更新了我的问题,显示了存储过程中的逻辑。 而且,我对我认为的问题的说法是相反的:|。我的意思是当你传入DBNull.Value时,存储过程认为它应该使用'default'参数值。您可以在更新的问题中看到存储过程逻辑。有没有办法表明某个参数已被“填写”?
  • 啊,所以我需要SELECT CASE WHEN ( @pSearch01 IS NULL or @pSearch01 != 'BTR:NOSEARCHUPDATE' ) THEN @pSearch01 ELSE 'ValueFromDB' END search01之类的东西?
猜你喜欢
  • 1970-01-01
  • 2011-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-28
  • 1970-01-01
相关资源
最近更新 更多