إنشاء تشغيل خادم SQL للانتقال من مفتاح طبيعي إلى مفتاح بديل

StackOverflow https://stackoverflow.com/questions/3938287

سؤال

خلفية

في العمل حيث نخطط لإهمال عمود مفتاح طبيعي في أحد الجداول الأساسية لدينا. يتكون المشروع من أكثر من 100 تطبيق يرتبط بهذا الجدول/العمود ؛ 400+ الإجراءات المخزنة التي تشير إلى هذا العمود مباشرة ؛ ومجموعة واسعة من الجداول الشائعة بين هذه التطبيقات التي تشير أيضًا إلى هذا العمود.

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

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

ها هو أساسي مثال على ما لدينا:

Users
---------------------------
Code          varchar(32) natural key

Access
---------------------------
UserCode      varchar(32) foreign key
AccessLevel   int

ونحن نهدف الآن فقط إلى حالة انتقالية مثل هذا:

Users
---------------------------
Code          varchar(32) 
Id            int         surrogate key

Access
---------------------------
UserCode      varchar(32)   
UserID        int         foreign key      
AccessLevel   int

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

أردت استخدام مشغلات SQL Server لاعتراض أي إدراج/تحديث جديد تلقائيًا والقيام بشيء مثل ما يلي على كل من الجداول المتأثرة:

CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT(, UPDATE)
AS
BEGIN
  DIM @code as Varchar(32)
  DIM @id as int

  SET @code = (SELECT inserted.code FROM inserted)
  SET @id = (SELECT inserted.code FROM inserted)

  -- This is a migrated application; find the appropriate legacy key
  IF @code IS NULL AND @id IS NOT NULL
     SELECT Code FROM Users WHERE Users.id = @id

  -- This is a legacy application; find the appropriate surrogate key
  IF @id IS NULL AND @code IS NOT NULL
     SELECT Code FROM Users WHERE Users.id = @id

  -- Impossible code:
  UPDATE inserted SET inserted.code=@code, inserted.id=@id
END

سؤال

المشاكل الضخمة التي أواجهها حتى الآن هي:

  1. لا يمكنني القيام بـ "بعد الإدراج" لأن القيود الفارغة ستجعل الإدراج تفشل.
  2. "الرمز المستحيل" الذي ذكرته هو كيف أرغب في تنظيف الاستعلام الأصلي ؛ إذا كان الاستعلام الأصلي يحتوي على أعمدة x و y أو z فيه أو فقط x ، فأنا أرغب بشكل مثالي في القيام بذلك. وإذا قمت بإضافة/حذف عمود آخر ، فأود أن يظل الزناد وظيفيًا.

أي شخص لديه مثال رمز حيث يمكن أن يكون ذلك ممكنًا ، أو حتى حلًا بديلًا للحفاظ على هذه الأعمدة مملوءة بشكل صحيح حتى عندما يتم تمرير واحد فقط من القيم إلى SQL؟

هل كانت مفيدة؟

المحلول 3

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

CREATE TRIGGER tr_Access_Sync
ON Access
INSTEAD OF INSERT
AS 
BEGIN

    /*-- Create a temporary table to modify because "inserted" is read-only */
    /*-- "temp" is actually "#temp" but it throws off stackoverflow's syntax highlighting */
    SELECT * INTO temp FROM inserted

    /*-- If for whatever reason the secondary table has it's own identity column */
    /*-- we need to get rid of it from our #temp table to do an Insert later with identities on */
    ALTER TABLE temp DROP COLUMN oneToManyIdentity

    UPDATE temp 
    SET 
        UserCode = ISNULL(UserCode, (SELECT UserCode FROM Users U WHERE U.UserID = temp.UserID)),
        UserID = ISNULL(UserID, (SELECT UserID FROM Users U WHERE U.UserCode = temp.UserCode))

    INSERT INTO Access SELECT * FROM temp

END

نصائح أخرى

الأعمال صعبة...

حسنًا ، أولاً وقبل كل شيء: هذا المشغل سوف ليس العمل في العديد من الظروف:

SET @code = (SELECT inserted.code FROM inserted)
SET @id = (SELECT inserted.code FROM inserted)

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

النقطة الثانية: سأحاول صنع الهوية IDENTITY الحقول - ثم سيحصلون دائمًا على قيمة - حتى بالنسبة لتطبيقات "Legacy". يجب أن توفر تلك التطبيقات "القديمة" مفتاحًا قديمًا بدلاً من ذلك - لذلك يجب أن تكون بخير هناك. المشكلة الوحيدة التي أراها ولا أعرف كيف تتعامل مع هذه الإدراج من تطبيق تم تحويله بالفعل - هل يوفرون مفتاحًا قديمًا "على الطراز القديم" أيضًا؟ إذا لم يكن الأمر كذلك - ما مدى سرعة حاجة إلى مثل هذا المفتاح؟

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

ألن يكون من الممكن جعل المخطط يغير "Bigbang" ولكن إنشاء طرق عرض على الجزء العلوي من تلك الجداول التي "تخفي" التغيير؟

أعتقد أنك قد تجد أنك ببساطة تؤجل الكسر إلى نقطة لاحقة: "سنقوم بتهدئة هذا العمود على تطبيق واحد في وقت واحد" - قد يكون ذلك سذاجتي ولكن لا يمكنني رؤية كيف يحدث ذلك على الإطلاق للعمل.

بالتأكيد ، يمكن أن تحدث فوضى أسوأ عندما تقوم التطبيقات المختلفة بأشياء مختلفة؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top