在 SQLServer 2005 函数中执行动态 SQL

Executing dynamic SQL in a SQLServer 2005 function

提问人:AJD 提问时间:9/30/2008 最后编辑:RobAJD 更新时间:11/20/2015 访问量:99523

问:

我在这个问题的开头要说的是,我不认为这是可以解决的。我还有一个解决方法,我可以创建一个带有 OUTPUT 的存储过程来完成此操作,使用函数对需要此校验和的部分进行编码会更容易。

由于调用,此代码将不起作用。有人知道如何在函数中执行动态SQL吗?(再一次,我认为这是不可能的。如果是这样,我很想知道如何绕过它!Exec SP_ExecuteSQL @SQL

Create Function Get_Checksum
(
    @DatabaseName      varchar(100),
    @TableName         varchar(100)
)
RETURNS FLOAT
AS
BEGIN

 Declare @SQL        nvarchar(4000)
 Declare @ColumnName varchar(100)
 Declare @i          int
 Declare @Checksum   float
 Declare @intColumns table (idRecord int identity(1,1), ColumnName varchar(255))
 Declare @CS         table (MyCheckSum bigint)

 Set @SQL = 
        'Insert Into @IntColumns(ColumnName)' + Char(13) + 
        'Select Column_Name' + Char(13) +
        'From   ' + @DatabaseName + '.Information_Schema.Columns (NOLOCK)' + Char(13) +
        'Where  Table_Name = ''' + @TableName + '''' + Char(13) +
        '       and Data_Type = ''int''' 

 -- print @SQL

 exec sp_executeSql @SQL

 Set @SQL = 
        'Insert Into @CS(MyChecksum)' + Char(13) + 
        'Select '

 Set @i = 1

 While Exists(
       Select 1
       From   @IntColumns
       Where  IdRecord = @i)
 begin
       Select @ColumnName = ColumnName
       From   @IntColumns
       Where  IdRecord = @i

       Set @SQL = @SQL + Char(13) + 
            CASE WHEN @i = 1 THEN 
                 '    Sum(Cast(IsNull(' + @ColumnName + ',0) as bigint))'
                 ELSE
                 '    + Sum(Cast(IsNull(' + @ColumnName + ',0) as bigint))'
            END

       Set @i = @i + 1
 end

 Set @SQL = @SQL + Char(13) + 
      'From ' + @DatabaseName + '..' + @TableName + ' (NOLOCK)'

 -- print @SQL

 exec sp_executeSql @SQL

 Set @Checksum = (Select Top 1 MyChecksum From @CS)

 Return isnull(@Checksum,0)

END
GO
sql-server-2005 函数

评论


答:

2赞 Cade Roux 9/30/2008 #1

您可以通过调用扩展存储过程来解决此问题,以及随之而来的所有麻烦和安全问题。

http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/

http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/

0赞 ConcernedOfTunbridgeWells 9/30/2008 #2

由于函数必须与查询优化器很好地配合,因此对它们有相当多的限制。此链接引用了一篇深入讨论 UDF 局限性的文章。

17赞 Rob 9/30/2008 #3

它“通常”无法完成,因为 SQL Server 将函数视为确定性函数,这意味着对于给定的一组输入,它应该始终返回相同的输出。存储过程或动态 SQL 可以是不确定的,因为它可以更改所依赖的外部状态,例如表。

鉴于 SQL Server 中的函数始终是确定性的,从将来的维护角度来看,尝试规避这一点将是一个坏主意,因为它可能会给将来必须支持代码的任何人带来相当大的混乱。

评论

17赞 Stefan Steiger 12/21/2011
SQL服务器函数当然并不总是确定性的,例如getdate()是非确定性的(取决于语言)......
0赞 AJD 10/1/2008 #4

谢谢大家的回复。

罗恩:仅供参考,使用它会引发错误。

我同意不做我最初打算做的事情是最好的解决方案,我决定走一条不同的路线。我的两个选择是在存储过程中使用 或输出参数。在对每个表进行单元测试后,我决定为每个表的数据获取一个可比较的校验和值。sum(cast(BINARY_CHECKSUM(*) as float))sum(cast(BINARY_CHECKSUM(*) as float))

评论

0赞 Ron Savage 6/29/2010
错误是因为 exec() 调用有自己的作用域 - 并且变量未在其中定义(如表变量)。您需要使用将在整个范围内生存的真实表或全局临时表。
4赞 Praveen Kumar G 9/15/2012 #5

这是解决方案

解决方案 1:然后从 Function 返回动态字符串

Declare @SQLStr varchar(max) 
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)

select * from @tmptable

解决方案二:通过传递参数调用嵌套函数。

评论

3赞 Mosta 5/5/2017
它不起作用:在函数中无效使用副作用运算符“INSERT EXEC”。