Je préfère cette question en disant: je ne pense pas qu'elle soit résoluble. J'ai également une solution de contournement, je peux créer une procédure stockée avec une sortie pour accomplir cela, il est juste plus facile de coder les sections où j'ai besoin de cette somme de contrôle en utilisant une fonction.
Ce code ne fonctionnera pas à cause du Exec SP_ExecuteSQL @SQL
appels. Quelqu'un sait comment exécuter du SQL dynamique dans une fonction? (et encore une fois, je ne pense pas que ce soit possible. Si c'est le cas, j'aimerais savoir comment le contourner!)
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
Cela "normalement" ne peut pas être fait car SQL Server traite les fonctions comme déterministes, ce qui signifie que pour un ensemble donné d'entrées, il doit toujours renvoyer les mêmes sorties. Une procédure stockée ou SQL dynamique peut être non déterministe car elle peut changer l'état externe, comme une table, sur laquelle on s'appuie.
Étant donné que dans les fonctions de serveur SQL sont toujours déterministes, ce serait une mauvaise idée dans une perspective de maintenance future d'essayer de contourner cela car cela pourrait causer une confusion assez importante pour quiconque doit prendre en charge le code à l'avenir.
Voici la solution
Solution 1: Renvoie la chaîne dynamique de Function puis
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
Solution 2: appeler des fonctions imbriquées en passant des paramètres.
Vous pouvez contourner ce problème en appelant une procédure stockée étendue, avec tous les tracas et problèmes de sécurité qui en découlent.
http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/
http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/
Étant donné que les fonctions doivent fonctionner correctement avec l'optimiseur de requêtes, elles sont soumises à de nombreuses restrictions. Ce lien fait référence à un article qui traite des limites des UDF en profondeur.
Merci à tous pour les réponses.
Ron: Pour info, utiliser cela va générer une erreur.
Je suis d'accord que ne pas faire ce que je voulais à l'origine est la meilleure solution, j'ai décidé d'emprunter une voie différente. Mes deux choix étaient d'utiliser sum(cast(BINARY_CHECKSUM(*) as float))
ou un paramètre de sortie dans une procédure stockée. Après avoir testé la vitesse de chaque unité, j'ai décidé d'utiliser sum(cast(BINARY_CHECKSUM(*) as float))
pour obtenir une valeur de somme de contrôle comparable pour les données de chaque table.