在 SQLServer 2005 函数中执行动态 SQL
Posted
技术标签:
【中文标题】在 SQLServer 2005 函数中执行动态 SQL【英文标题】:Executing dynamic SQL in a SQLServer 2005 function 【发布时间】:2008-09-29 20:43:45 【问题描述】:我会先说这个问题,我不认为这是可以解决的。我也有一个解决方法,我可以创建一个带有 OUTPUT 的存储过程来完成此操作,使用函数对需要此校验和的部分进行编码更容易。
由于Exec SP_ExecuteSQL @SQL
调用,此代码将不起作用。有人知道如何在函数中执行动态 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
【问题讨论】:
【参考方案1】:“通常”无法做到这一点,因为 SQL Server 将函数视为确定性,这意味着对于给定的一组输入,它应该始终返回相同的输出。存储过程或动态 sql 可以是不确定的,因为它可以更改依赖的外部状态,例如表。
鉴于 SQL Server 中的函数始终是确定性的,从未来维护的角度来看,试图规避这一点是一个坏主意,因为这可能会给将来必须支持该代码的任何人造成相当大的混乱。
【讨论】:
SQL-server 函数当然并不总是确定性的,例如 getdate() 是非确定性的(取决于语言)...【参考方案2】:解决办法
解决方案 1: 然后从 Function 返回动态字符串
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
解决方案 2: 通过传递参数调用嵌套函数。
【讨论】:
它不起作用:在函数中无效使用副作用运算符“INSERT EXEC”。【参考方案3】:您可以通过调用扩展存储过程来解决此问题,但会带来所有随之而来的麻烦和安全问题。
http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/
http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/
【讨论】:
【参考方案4】:因为函数必须与查询优化器很好地配合,所以对它们有很多限制。 This link 引用了一篇文章,深入讨论了 UDF 的局限性。
【讨论】:
【参考方案5】:谢谢大家的回复。
Ron:仅供参考,使用它会引发错误。
我同意不做我原本打算的事情是最好的解决方案,我决定走另一条路。我的两个选择是在存储过程中使用sum(cast(BINARY_CHECKSUM(*) as float))
或输出参数。在对每个表的数据进行单元测试后,我决定使用sum(cast(BINARY_CHECKSUM(*) as float))
来为每个表的数据获取可比较的校验和值。
【讨论】:
错误是因为 exec() 调用有它自己的范围 - 并且变量没有在其中定义(如表变量)。您需要使用跨范围的真实表或全局临时表。以上是关于在 SQLServer 2005 函数中执行动态 SQL的主要内容,如果未能解决你的问题,请参考以下文章
SQL Server 2005 - 在没有总和/计数和动态值列表的情况下透视数据
在存储过程 sql server 2005 中使用函数调用?