从 Dynamic SQL 调用存储过程或函数 - 使用可为 Null 的参数

Call a Stored Procedure or Function from Dynamic SQL - with Nullable Parameter

我正在尝试调用接受可为空参数的 sql 函数 - 来自动态 SQL 语句

创建动态语句很困难,因为当参数值为 'NULL' 时,连接会导致整个语句为空。我有以下内容:

SET dynamicQuery = 
   'select * from [qlik].udf_getStatistic( ''' + @myParameter + ''' )'

上面的示例位于传递@myParameter 的存储过程中。它可能为 null 或字符串值。显然,当它是一个字符串时需要用引号括起来,但是当它是 null 时就不能用引号括起来。如下:

select * from [qlik].udf_getStatistic( 'Heights' )

select * from [qlik].udf_getStatistic( NULL )

该问题同样适用于调用 存储过程 从动态 SQL 接受可为 null 的参数。 这些示例来自 SQL 服务器。

存储过程和函数之间的答案实际上是不同的。

来自 Books On Line or whatever they call it this month(向下滚动一种方式):

When a parameter of the function has a default value, the keyword DEFAULT must be specified when the function is called to retrieve the default value. This behavior is different from using parameters with default values in stored procedures in which omitting the parameter also implies the default value. However, the DEFAULT keyword is not required when invoking a scalar function by using the EXECUTE statement.

所以对于一个proc,当你想传一个NULL参数的时候,你可以不传。但是,对于函数,您必须明确告诉它使用 DEFAULT 值。无论哪种方式,您都不会显式传递给它 NULL。幸运的是,对于您的动态 SQL,显式 DEFAULT 也适用于存储过程。在这两种情况下,为了确保您 正在 传递的参数得到正确分配,您需要在调用中使用显式参数名称。

让我们使用这个函数定义:

CREATE FUNCTION (or procedure) [qlik].udf_getStatistic (
  @param1 integer = 0,
  @param2 varchar(100) = 'foo'
) AS ...

两个参数都是可选的。由于这是一个函数,此调用将引发 insufficient number of parameters 错误:

select * from [qlik].udf_getStatistic( 'Heights' );

如果它是一个过程调用,它会抛出一个 cannot convert value 'Heights' to data type integer 因为它将应用传递给它遇到的第一个参数的唯一参数值,它需要一个整数。在这两种情况下,您都可以通过这种方式得到您想要的:

select * from [qlik].udf_getStatistic( @param1 = DEFAULT, @param2 = 'Heights' );

这让我们看到了您的动态 SQL。将您的参数名称添加到静态文本,然后使用 COALESCE(或 CASE,如果您愿意)决定是传递显式值,还是调用 DEFAULT

DECLARE @myParameter1 VARCHAR(100) = 'foo',
        @myParameter2 INTEGER,
        @SQL NVARCHAR(MAX);


SET @SQL = 
   'select 
      * 
    from [qlik].udf_getStatistic( 
      @param1 = ''' + COALESCE(@myParameter1, 'DEFAULT') + ''', 
      @param2 = ' + COALESCE(CAST(@myParameter2 AS VARCHAR(30)),'DEFAULT') + ' );';

SELECT @SQL;

结果:

select * from [qlik].udf_getStatistic( @param1 = 'foo', @param2 = DEFAULT );

只需使用显式文字 NULL 转义 NULL 值,确保仅在值不是 NULL.

时才包含引号
DECLARE @myParameter VARCHAR(10) = 'ABC'

DECLARE @dynamicQuery VARCHAR(MAX)

SET @dynamicQuery = 
   'select * from [qlik].udf_getStatistic(' + ISNULL('''' + @myParameter + '''', 'NULL') + ')'


SELECT @dynamicQuery -- select * from [qlik].udf_getStatistic('ABC')

SET @myParameter = NULL

SET @dynamicQuery = 
   'select * from [qlik].udf_getStatistic(' + ISNULL('''' + @myParameter + '''', 'NULL') + ')'

SELECT @dynamicQuery -- select * from [qlik].udf_getStatistic(NULL)

您可能想要转义变量上可能存在的其他单引号,将其替换为双单引号,这样就不会破坏您的动态构建。

根据我的理解,我在 SQL Server 2012 上尝试这个,

CREATE PROCEDURE ToNullProc 
     (@i VARCHAR(20)) 
AS 
BEGIN
    PRINT 'you entered ' + @i
END

CREATE FUNCTION ToNullFun 
     (@i VARCHAR(20)) 
RETURNS @table TABLE (i VARCHAR(20))
AS
BEGIN
    INSERT INTO @table 
        SELECT ('You entered ' + @i) a
    RETURN
END

DECLARE @j VARCHAR(20) = 'Hi',
        @QueryFun NVARCHAR(50) = N'',
        @QueryProd NVARCHAR(50) = N''

IF @j IS NOT NULL
BEGIN
    SET @QueryFun = N'select * from ToNullFun ('''+@j+''')'
    SET @QueryProd = N'exec ToNullProc '''+@j+''''
END
ELSE BEGIN
   SET @QueryFun = N'select * from ToNullFun ('+@j+')'
   SET @QueryProd = N'exec ToNullProc '+@j+''
END

PRINT @queryfun
PRINT @queryprod

EXEC sp_executesql @queryfun
EXEC sp_executesql @queryprod

更新 动态过程和动态函数 :

create table #temp (Num int identity (1,1), NullVal int)
insert into #temp (NullVal) values (1),(null),(3)

alter proc ToNullProc (
 @Operator varchar (max), @NullVal varchar (max)
) as
begin 

 declare @Query nvarchar (max) = N'select * from #temp where NullVal ' + 
  @Operator + @NullVal 
 -- print @query + ' ToNullProc print '

 exec sp_executesql @query  -- Here we run the select query from Proc

end

create function ToNullFun (
 @Operator varchar (max), @NullVal varchar (max)
) 
returns nvarchar (max)
as
begin    
 declare @Query nvarchar (max)

 set @Query = N'select * from #temp where NullVal ' + @Operator + @NullVal 

 /* 
  I try to into to Table variable by using ITVF,
  'insert into @table exec sp_executesql @query'.
  But this type of insert is not allowed in ITVF.
*/

 return @query

end

declare @NullVal varchar (max) = '1'
, @QueryFun nvarchar (max) = N''
, @QueryProd nvarchar (max) = N''
declare @FunResultTalbe table (
 Query nvarchar (100)
) /* To store the result Funtion */
if @NullVal is not null
begin
 set @QueryFun = N'select dbo.ToNullFun ('' = '','''+@NullVal+''')'
 set @QueryProd = N'exec ToNullProc '' = '','''+@NullVal+''''
end
else begin 
 set @QueryFun = N'select dbo.ToNullFun ('' is null '','''')'
 set @QueryProd = N'exec ToNullProc '' is null '','''''
end

print  @queryfun + ' At start'
print  @queryprod + ' At start'

exec sp_executesql @queryprod  -- It calls Proc 

insert into @FunResultTalbe
exec sp_executesql @queryfun   -- It calls the Function and insert the query into the table.

set @QueryFun = (select top 1 * from @FunResultTalbe)  -- Here we get the query from the table.
print @queryfun

exec sp_executesql @queryfun  -- Here we run the select query. Which is dynamic

结果集

-- Result of Procedure
Num NullVal
1   1

-- Result of Function
Num NullVal
1   1

告诉我,你得到了什么。