web-dev-qa-db-fra.com

Exécution de SQL dynamique dans une fonction SQLServer 2005

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
20
AJD

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.

15
Rob

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.

5
Praveen Kumar G

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/

2
Cade Roux

É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.

0
AJD