Выполнение динамического SQL в функции SQLServer 2005
-
02-07-2019 - |
Вопрос
Я предварю этот вопрос словами: "Я не думаю, что он разрешим".У меня также есть обходной путь, я могу создать хранимую процедуру с ВЫВОДОМ для достижения этой цели, просто проще закодировать разделы, где мне нужна эта контрольная сумма, используя функцию.
Этот код не будет работать из-за 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
Решение
Это "обычно" невозможно выполнить, поскольку SQL Server рассматривает функции как детерминированные, что означает, что для данного набора входных данных он всегда должен возвращать одни и те же выходные данные.Хранимая процедура или динамический sql могут быть недетерминированными, поскольку они могут изменять внешнее состояние, например таблицу, на которую полагаются.
Учитывая, что в SQL server функции всегда детерминированы, с точки зрения будущего обслуживания было бы плохой идеей пытаться обойти это, поскольку это может вызвать довольно серьезную путаницу у любого, кому придется поддерживать код в будущем.
Другие советы
Вот решение
Решение 1: Затем верните динамическую строку из функции
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
Решение 2: вызывайте вложенные функции, передавая параметры.
Вы можете обойти это, вызвав расширенную хранимую процедуру со всеми сопутствующими хлопотами и проблемами безопасности.
http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/
http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/
Поскольку функции должны хорошо взаимодействовать с оптимизатором запросов, на них накладывается довольно много ограничений. Эта ссылка ссылается на статью, в которой подробно обсуждаются ограничения UDF.
Спасибо вам всем за ответы.
Рон:К вашему сведению, использование этого приведет к выдаче ошибки.
Я согласен, что не делать то, что я изначально намеревался, - лучшее решение, поэтому я решил пойти другим путем.Моими двумя вариантами были использовать sum(cast(BINARY_CHECKSUM(*) as float))
или выходной параметр в хранимой процедуре.После модульного тестирования скорости каждого из них я решил использовать sum(cast(BINARY_CHECKSUM(*) as float))
чтобы получить сопоставимое значение контрольной суммы для данных каждой таблицы.