Выполнение динамического SQL в функции SQLServer 2005
Я предопределю этот вопрос, сказав: я не думаю, что он разрешимый. У меня также есть обходной путь, я могу создать хранимую процедуру с помощью 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-сервере функции всегда детерминированы, было бы плохой идеей из перспективы будущего обслуживания попытаться обойти это, поскольку это может вызвать довольно серьезную путаницу для всех, кто должен поддерживать код в будущем.
Ответ 2
Вот решение
Решение 1:
Верните динамическую строку из функции, затем
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
Решение 2:
вызывать вложенные функции путем передачи параметров.
Ответ 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
Поскольку функции должны хорошо работать с оптимизатором запросов, на них существует немало ограничений. Эта ссылка относится к статье, в которой подробно рассматриваются ограничения UDF.
Ответ 5
Спасибо всем за ответы.
Ron: FYI, используя это, выдает ошибку.
Я согласен с тем, что не делать то, что я изначально планировал, это лучшее решение, я решил пойти другим путем. Мои два варианта заключались в использовании sum(cast(BINARY_CHECKSUM(*) as float))
или выходном параметре в хранимой процедуре. После единичного тестирования каждой из них я решил пойти с sum(cast(BINARY_CHECKSUM(*) as float))
, чтобы получить сопоставимое значение контрольной суммы для каждой таблицы данных.