تنفيذ SQL الديناميكي في دالة SQLServer 2005
-
02-07-2019 - |
سؤال
سأستهل هذا السؤال بالقول، لا أعتقد أنه قابل للحل.لدي أيضًا حل بديل، يمكنني إنشاء إجراء مخزن باستخدام OUTPUT لإنجاز ذلك، فمن الأسهل ترميز الأقسام التي أحتاج فيها إلى هذا المجموع الاختباري باستخدام دالة.
لن يعمل هذا الرمز بسبب Exec SP_ExecuteSQL @SQL
المكالمات.هل يعرف أحد كيفية تنفيذ SQL الديناميكي في إحدى الوظائف؟(ومرة أخرى، لا أعتقد أن هذا ممكن.إذا كان الأمر كذلك، فأنا أحب أن أعرف كيفية التغلب عليه!)
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
المحلول
لا يمكن القيام بذلك "عادة" لأن SQL Server يعامل الوظائف على أنها حتمية، مما يعني أنه بالنسبة لمجموعة معينة من المدخلات، يجب دائمًا إرجاع نفس المخرجات.يمكن أن يكون الإجراء المخزن أو SQL الديناميكي غير حتمي لأنه يمكن أن يغير الحالة الخارجية، مثل الجدول الذي يتم الاعتماد عليه.
نظرًا لأن وظائف خادم SQL تكون دائمًا حتمية، فسيكون من السيئ من منظور الصيانة المستقبلية محاولة التحايل على هذا لأنه قد يسبب ارتباكًا كبيرًا إلى حد ما لأي شخص يتعين عليه دعم الكود في المستقبل.
نصائح أخرى
هنا هو الحل
الحل 1:قم بإرجاع السلسلة الديناميكية من الوظيفة بعد ذلك
Declare @SQLStr varchar(max)
DECLARE @tmptable table (<columns>)
set @SQLStr=dbo.function(<parameters>)
insert into @tmptable
Exec (@SQLStr)
select * from @tmptable
الحل 2:استدعاء الوظائف المتداخلة عن طريق تمرير المعلمات.
يمكنك التغلب على ذلك عن طريق استدعاء إجراء مخزن موسع، مع كل ما يصاحب ذلك من متاعب ومشكلات أمنية.
http://decipherinfosys.wordpress.com/2008/07/16/udf-limitations-in-sql-server/
http://decipherinfosys.wordpress.com/2007/02/27/using-getdate-in-a-udf/
نظرًا لأن الوظائف يجب أن تعمل بشكل جيد مع مُحسِّن الاستعلام، فهناك عدد غير قليل من القيود عليها. هذا الرابط يشير إلى مقال يناقش القيود المفروضة على UDF بعمق.
شكرا لكم جميعا على الردود.
رون:لمعلوماتك، سيؤدي استخدام ذلك إلى حدوث خطأ.
أوافق على أن عدم القيام بما كنت أنويه في الأصل هو الحل الأفضل، فقررت أن أسلك طريقًا مختلفًا.كان خياري اثنين للاستخدام sum(cast(BINARY_CHECKSUM(*) as float))
أو معلمة إخراج في إجراء مخزن.بعد اختبار سرعة الوحدة لكل منها، قررت أن أذهب معها sum(cast(BINARY_CHECKSUM(*) as float))
للحصول على قيمة مجموع اختباري قابلة للمقارنة لبيانات كل جدول.