لماذا تظهر رسالة "The query processor ran out of stack space during query optimization" عند حذف حساب؟

Viewed 4

عند محاولة حذف حساب من دليل الحسابات تظهر رسالة الخطأ التالية ويفشل الحذف:

Could not perform database operation, reason: The query processor ran out of stack space during query optimization. Please simplify the query.

الحساب المطلوب حذفه لا توجد عليه حركة، والمشكلة تتكرر مع أكثر من حساب. ما سبب هذه الرسالة وكيف يمكن حلها؟

1 Answers

هذه الرسالة لا علاقة لها بإعدادات النظام ولا برصيد الحساب، وإنما تأتي من SQL Server نفسه، وسببها مشكلة قديمة موروثة في قواعد البيانات المنشأة منذ سنوات.

في التصميم القديم كانت حقول الحسابات الموجودة في العملاء والموردين والموظفين وباقي أنواع الذمم تُنشأ كمفاتيح خارجية (Foreign Keys) حقيقية تشير إلى جدول Account. وبما أن الحساب موجود تقريباً في كل جداول قاعدة البيانات، فإن أمر الحذف الذي يبنيه SQL Server يضطر إلى فحص مئات الجداول المرتبطة في وقت واحد، فينفد من مُحسِّن الاستعلام (Query Optimizer) الحيز المخصص له أثناء تخطيط الاستعلام، فتظهر هذه الرسالة.

ونفس المشكلة بالضبط كانت تحدث عند حذف المستخدمين والموظفين والمرفقات، ولهذا السبب تم تغيير التصميم بحيث لم تعد تُنشأ قيود على مستوى قاعدة البيانات للحقول التي تشير إلى جداول موجودة في كل الكيانات تقريباً (الحسابات، المستخدمون، الموظفون، المرفقات). لذلك لا تظهر المشكلة في قواعد البيانات الجديدة، أما قاعدة البيانات القديمة فما زالت تحمل تلك القيود.

الحل هو حذف المفاتيح الخارجية المتبقية التي تشير إلى الحسابات، وذلك بتنفيذ السكربت التالي مرة واحدة على قاعدة البيانات:

while(exists(select * from INFORMATION_SCHEMA.TABLE_CONSTRAINTS t left join INFORMATION_SCHEMA.KEY_COLUMN_USAGE k on k.CONSTRAINT_NAME = t.CONSTRAINT_NAME
where CONSTRAINT_TYPE='FOREIGN KEY' and COLUMN_NAME in ('mainAccount_id','account1_id','account2_id','account3_id','account4_id','account5_id','account6_id','account7_id','account8_id','account9_id','account10_id','account11_id','account12_id','account13_id','account14_id','account15_id','account16_id','account17_id','account18_id','account19_id','account20_id','account_id')))
begin
 declare @sql nvarchar(2000)
 SELECT TOP 1 @sql=('ALTER TABLE ' + t.TABLE_SCHEMA + '.[' + t.TABLE_NAME
           + '] DROP CONSTRAINT [' + t.CONSTRAINT_NAME + ']')
 from INFORMATION_SCHEMA.TABLE_CONSTRAINTS t left join INFORMATION_SCHEMA.KEY_COLUMN_USAGE k on k.CONSTRAINT_NAME = t.CONSTRAINT_NAME
 where CONSTRAINT_TYPE='FOREIGN KEY' and COLUMN_NAME in ('mainAccount_id','account1_id','account2_id','account3_id','account4_id','account5_id','account6_id','account7_id','account8_id','account9_id','account10_id','account11_id','account12_id','account13_id','account14_id','account15_id','account16_id','account17_id','account18_id','account19_id','account20_id','account_id')
 exec (@sql)
end

وهذا الإجراء آمن، لأن النظام يتحقق من صحة هذه العلاقات داخل التطبيق نفسه وليس عن طريق قيود قاعدة البيانات. بعد تنفيذ السكربت أعد محاولة حذف الحساب ولن تظهر الرسالة مرة أخرى.

ملاحظة: خذ نسخة احتياطية من قاعدة البيانات قبل تنفيذ أي تعديل على الـ schema، ونفّذ السكربت خارج أوقات العمل إن أمكن.

إذا ظهرت نفس الرسالة عند حذف مستخدم أو موظف أو مرفق، فهناك سكربت مماثل لكل حالة في صفحة العمليات المتعلقة بقاعدة البيانات: