Execução de SQL dinâmica em uma função SQLServer 2005
-
02-07-2019 - |
Pergunta
Vou prefácio a esta pergunta dizendo, eu não acho que isso pode ser resolvido. Eu também tenho uma solução alternativa, eu posso criar um procedimento armazenado com uma produção para alcançar este objetivo, é apenas mais fácil de codificar as seções onde eu preciso esta soma de verificação utilizando uma função.
Este código não vai funcionar por causa das chamadas Exec SP_ExecuteSQL @SQL
. Alguém sabe como executar SQL dinâmico em uma função? (E mais uma vez, eu não acho que isso é possível. Se for embora, eu adoraria saber como contornar isso!)
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
Solução
It "normalmente" não pode ser feito como SQL Server trata funciona como determinista, o que significa que para um determinado conjunto de entradas, deve sempre retornam os mesmos resultados. Um procedimento armazenado ou SQL dinâmico pode ser não-determinista, porque pode alterar o estado externo, como uma mesa, que é invocado.
Tendo em conta que nas funções de servidor SQL são sempre determinista, seria uma má idéia de uma futura perspectiva de manutenção para tentar contornar isso, pois poderia causar bastante grande confusão para quem tem de suportar o código no futuro.
Outras dicas
Aqui está a solução
Solução 1: Retornar a string dinâmica de função, então
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
Solução 2: chamar funções aninhadas passando parâmetros.
Você pode contornar este problema chamando um procedimento armazenado estendido, com todos os problemas de aborrecimento e de segurança inerentes.
http: //decipherinfosys.wordpress. com / 2008/07/16 /-UDF limitações-in sql-server /
http: //decipherinfosys.wordpress. cOM / 2007/02/27 / utilizando-getdate-in-a-UDF /
Porque funções tem que jogar bem com o otimizador de consulta, existem algumas restrições sobre eles. Esta ligação refere para um artigo que discute as limitações do UDF de em profundidade.
Obrigado a todos pelas respostas.
Ron:. FYI, Usando que irá lançar um erro
Eu concordo que não fazer o que eu originalmente planejado é a melhor solução, eu decidi ir para um caminho diferente. Minhas duas opções eram de usar sum(cast(BINARY_CHECKSUM(*) as float))
ou um parâmetro de saída de um procedimento armazenado. Após a unidade de teste de velocidade de cada um, eu decidi ir com sum(cast(BINARY_CHECKSUM(*) as float))
para obter um valor de checksum comparável para os dados de cada tabela.