Ejecutar SQL dinámico en una función SQLServer 2005
-
02-07-2019 - |
Pregunta
Prefacio a esta pregunta diciendo: No creo que sea solucionable. También tengo una solución alternativa, puedo crear un procedimiento almacenado con una SALIDA para lograr esto, es más fácil codificar las secciones donde necesito esta suma de verificación usando una función.
Este código no funcionará debido a las llamadas Exec SP_ExecuteSQL @SQL
. ¿Alguien sabe cómo ejecutar SQL dinámico en una función? (y una vez más, no creo que sea posible. Si es así, ¡me encantaría saber cómo solucionarlo!)
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
Solución
Es " ordinariamente " no se puede hacer ya que SQL Server trata las funciones como deterministas, lo que significa que para un conjunto dado de entradas, siempre debe devolver las mismas salidas. Un procedimiento almacenado o sql dinámico puede ser no determinista porque puede cambiar el estado externo, como una tabla, en la que se confía.
Dado que en las funciones del servidor SQL siempre son deterministas, sería una mala idea desde una perspectiva de mantenimiento futuro intentar eludir esto, ya que podría causar una confusión bastante importante para cualquiera que tenga que soportar el código en el futuro.
Otros consejos
Aquí está la solución
Solución 1: Devuelva la cadena dinámica de Función y luego
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
Solución 2: llamar a funciones anidadas pasando parámetros.
Puede solucionar esto llamando a un procedimiento almacenado extendido, con todas las molestias y problemas de seguridad del asistente.
http: //decipherinfosys.wordpress. com / 2008/07/16 / udf-limits-in-sql-server /
http: //decipherinfosys.wordpress. com / 2007/02/27 / using-getdate-in-a-udf /
Debido a que las funciones tienen que funcionar bien con el optimizador de consultas, existen bastantes restricciones. Este enlace hace referencia a un artículo que analiza en profundidad las limitaciones de los UDF.
Gracias a todos por las respuestas.
Ron: Para tu información, usar eso arrojará un error.
Estoy de acuerdo en que no hacer lo que pretendía originalmente es la mejor solución, decidí tomar una ruta diferente. Mis dos opciones fueron usar sum (cast (BINARY_CHECKSUM (*) como flotante))
o un parámetro de salida en un procedimiento almacenado. Después de probar la velocidad de cada unidad, decidí ir con sum (cast (BINARY_CHECKSUM (*) as float))
para obtener un valor de suma de comprobación comparable para los datos de cada tabla.