Выполнение динамического 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: вызывать вложенные функции путем передачи параметров.

Ответ 4

Поскольку функции должны хорошо работать с оптимизатором запросов, на них существует немало ограничений. Эта ссылка относится к статье, в которой подробно рассматриваются ограничения UDF.

Ответ 5

Спасибо всем за ответы.

Ron: FYI, используя это, выдает ошибку.

Я согласен с тем, что не делать то, что я изначально планировал, это лучшее решение, я решил пойти другим путем. Мои два варианта заключались в использовании sum(cast(BINARY_CHECKSUM(*) as float)) или выходном параметре в хранимой процедуре. После единичного тестирования каждой из них я решил пойти с sum(cast(BINARY_CHECKSUM(*) as float)), чтобы получить сопоставимое значение контрольной суммы для каждой таблицы данных.