فهم مشكلة إدخال المشغل في SQL Server 2005

عند العمل مع SQL Server 2005، قد تواجه حالة حيث يقوم مشغلك بإدخال سجل واحد فقط في الجدول المستهدف على الرغم من أن عملية الإدخال الرئيسية تقوم بإنشاء سجلات متعددة. يمكن أن تكون هذه الحالة محبطة بشكل خاص، خاصة عند استخدام استعلام فرعي لإدخال البيانات في جدولك الرئيسي. في هذه المقالة، سنتناول مشكلة شائعة وحلها المحتمل بالتفصيل.

شرح المشكلة

تخيل أن لديك جدولًا يسمى tblMenuItems، وعند إدخال سجلات جديدة فيه، تريد من مشغلك، tblMenuItemInsertSecurity، إدخال سجلات تلقائيًا في جدول آخر (tblRestrictedMenuItems). يبدو أن المشغل يعمل بشكل جيد في الحالات التي يتم فيها إدخال سجل واحد؛ لكنه لا يعمل كما هو متوقع عندما يتسبب استعلام فرعي في إدخال سجلات متعددة دفعة واحدة.

إليك عبارة الإدخال

INSERT INTO [tblMenuItems] ([ID], [MenuID], [SortOrder], [ItemReference], [MenuReference], [ConcurrencyID]) 
SELECT [ID], [MenuID], [SortOrder], [ItemReference], [MenuReference], [ConcurrencyID] 
FROM [IVEEtblMenuItems]

مقتطف كود المشغل

CREATE TRIGGER [dbo].[tblMenuItemInsertSecurity] ON [dbo].[tblMenuItems] 
FOR INSERT
AS
BEGIN
    DECLARE @iRoleID int
    DECLARE @iMenuItemID int

    SELECT @iMenuItemID = [ID] FROM Inserted

    DECLARE tblUserRoles CURSOR FASTFORWARD FOR SELECT [ID] FROM tblUserRoles
    OPEN tblUserRoles 
    FETCH NEXT FROM tblUserRoles INTO @iRoleID 

    WHILE (@@FetchStatus = 0)
    BEGIN
        INSERT INTO tblRestrictedMenuItems(
            [RoleID],
            [MenuItemID],
            [RestrictLevel])
        VALUES(
            @iRoleID,
            @iMenuItemID,
            1)    

        FETCH NEXT FROM tblUserRoles INTO @iRoleID 
    END

    CLOSE tblUserRoles 
    DEALLOCATE tblUserRoles
END

فهم سبب فشل المشغل

السبب الرئيسي وراء عدم إدخال مشغلك سجلات متعددة يتعلق باستخدام الجدول الزائف Inserted. عندما يتم تفعيل المشغل، يحتوي الجدول Inserted على جميع الصفوف التي يتم إدخالها، ولكن منطق مشغلك يعالج بطريقة غير صحيحة الصف الأول فقط.

المفاهيم الأساسية لفهمها

  • المشغلات والصفوف: في SQL Server، سيتم تفعيل المشغل مرة واحدة لكل إجراء ت triggers (إدخال، تحديث، حذف) ويمكن أن يتعامل مع العمليات على عدة صفوف في وقت واحد.

  • الجدول الزائف Inserted: هذا الجدول يتيح الوصول إلى السجلات التي يتم إدخالها، ويحتوي على جميع الصفوف، وليس فقط الصف الأول.

الحل: تعديل منطق المشغل

للتعامل بفعالية مع الإدخالات لعدة سجلات، يمكنك إعادة هيكلة المشغل الخاص بك بطريقتين: استخدام حلقة مع مؤشر (والتي سنفصلها) أو من خلال استخدام العمليات القائمة على المجموعات، مما هو أكثر كفاءة.

1. إعادة الهيكلة باستخدام مؤشر

يمكنك تكرار الجدول الزائف Inserted باستخدام مؤشر، ولكن كن على دراية بأن هذه الطريقة قد تؤدي إلى مشاكل في الأداء مع مجموعات البيانات الكبيرة.

إليك نسخة معدلة من المشغل الخاص بك باستخدام مؤشر:

CREATE TRIGGER [dbo].[tblMenuItemInsertSecurity] ON [dbo].[tblMenuItems] 
FOR INSERT
AS
BEGIN
    DECLARE @iRoleID int
    DECLARE @iMenuItemID int

    DECLARE tblUserRoles CURSOR FASTFORWARD FOR SELECT [ID] FROM tblUserRoles
    OPEN tblUserRoles 
    FETCH NEXT FROM tblUserRoles INTO @iRoleID 

    -- حلقة عبر كل صف في الجدول Inserted
    DECLARE tblInserted CURSOR FOR SELECT [ID] FROM Inserted
    OPEN tblInserted 
    FETCH NEXT FROM tblInserted INTO @iMenuItemID 

    WHILE @@FETCH_STATUS = 0
    BEGIN
        WHILE (@@FetchStatus = 0)
        BEGIN
            INSERT INTO tblRestrictedMenuItems(
                [RoleID],
                [MenuItemID],
                [RestrictLevel])
            VALUES(
                @iRoleID,
                @iMenuItemID,
                1)    

            FETCH NEXT FROM tblUserRoles INTO @iRoleID 
        END

        FETCH NEXT FROM tblInserted INTO @iMenuItemID 
    END

    CLOSE tblInserted
    DEALLOCATE tblInserted
    CLOSE tblUserRoles 
    DEALLOCATE tblUserRoles
END

2. استخدام العمليات المستندة إلى المجموعات (مُوصى به)

هذه الطريقة أكثر كفاءة وتجنب الحلقات غير الضرورية.

تخيل تغيير المنطق للانضمام مباشرة إلى الجدول Inserted:

CREATE TRIGGER [dbo].[tblMenuItemInsertSecurity] ON [dbo].[tblMenuItems] 
FOR INSERT
AS
BEGIN
    INSERT INTO tblRestrictedMenuItems ([RoleID], [MenuItemID], [RestrictLevel])
    SELECT u.[ID], i.[ID], 1
    FROM tblUserRoles u
    JOIN Inserted i ON <condition if needed>
END

يضمن مقتطف الكود هذا أنه يتم إدخال عدد مناسب من السجلات في tblRestrictedMenuItems لكل صف في جدول Inserted.

الخاتمة

من خلال فهم سلوك المشغلات واستخدام الجدول الزائف Inserted بشكل صحيح، يمكنك ضمان أن تعمل مشغلات SQL Server 2005 الخاصة بك بشكل فعال مع إدخالات السجلات المتعددة. اختر بين النهجين المتكررين أو القائمين على المجموعات بناءً على سياقك المحدد لتحقيق الأداء الأمثل.

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