Esecuzione di SQL dinamico in una funzione SQLServer 2005
-
02-07-2019 - |
Domanda
Prefarrò questa domanda dicendo: non penso che sia risolvibile. Ho anche una soluzione alternativa, posso creare una procedura memorizzata con un OUTPUT per raggiungere questo obiettivo, è solo più facile codificare le sezioni in cui ho bisogno di questo checksum usando una funzione.
Questo codice non funzionerà a causa delle chiamate Exec SP_ExecuteSQL @SQL
. Qualcuno sa come eseguire SQL dinamico in una funzione? (e ancora una volta, non credo sia possibile. Se è così, mi piacerebbe sapere come aggirarlo!)
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
Soluzione
Si " ordinariamente " non può essere eseguito poiché SQL Server considera le funzioni come deterministiche, il che significa che per un determinato set di input, dovrebbe sempre restituire gli stessi output. Una procedura memorizzata o sql dinamico può essere non deterministico perché può cambiare lo stato esterno, ad esempio una tabella, su cui si fa affidamento.
Dato che le funzioni del server SQL sono sempre deterministiche, sarebbe una cattiva idea dal punto di vista della manutenzione futura tentare di aggirare ciò poiché potrebbe causare confusione abbastanza grande per chiunque debba supportare il codice in futuro.
Altri suggerimenti
Ecco la soluzione
Soluzione 1: Restituisce la stringa dinamica da Funzione quindi
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
Soluzione 2: chiama le funzioni nidificate passando parametri.
Puoi aggirare il problema chiamando una stored procedure estesa, con tutte le seccature e i problemi di sicurezza dell'operatore
http: //decipherinfosys.wordpress. com / 2008/07/16 / UDF-limiti-in-sql server /
http: //decipherinfosys.wordpress. com / 2007/02/27 / usando-getdate-in-a-UDF /
Poiché le funzioni devono funzionare bene con Query Optimizer, esistono alcune limitazioni. Questo link si riferisce a un articolo che discute in dettaglio i limiti dell'UDF.
Grazie a tutti per le risposte.
Ron: Cordiali saluti, Usarlo genererà un errore.
Sono d'accordo sul fatto che non fare ciò che inizialmente intendevo fosse la soluzione migliore, ho deciso di seguire una strada diversa. Le mie due scelte erano di usare sum (cast (BINARY_CHECKSUM (*) come float))
o un parametro di output in una procedura memorizzata. Dopo la velocità di test unitaria di ciascuno, ho deciso di scegliere sum (cast (BINARY_CHECKSUM (*) come float))
per ottenere un valore di checksum comparabile per i dati di ciascuna tabella.