سؤال

لقد فعلت هذا من قبل في مكان ما أنا متأكد من ذلك!

لدي SQL Server 2000 الجدول أنني بحاجة إلى تسجيل التغييرات على الحقول على التحديثات و يدخل في الثانية تسجيل الجدول.نسخة مبسطة من هيكل أنا باستخدام أدناه:

MainTable
ID varchar(10) PRIMARY KEY
DESCRIPTION varchar(50)

LogTable
OLDID varchar(10)
NEWID varchar(10)

عن أي مجال آخر شيء مثل هذا العمل العظيم:

Select i.DESCRIPTION As New, d.DESCRIPTION As Old 
From Inserted i
LEFT JOIN Deleted d On i.ID=d.ID

لكن من الواضح أن الانضمام سوف تفشل إذا كان معرف تغيرت.

أنا لا يمكن تعديل الجداول في طريقة توليد الطاقة الوحيدة لدي في قاعدة البيانات هذه هو إنشاء الزناد.

بدلا من ذلك هل هناك شخص يمكن أن تعلمني وقت السفر و سأعود إلى الماضي و أسأل نفسي مرة أخرى ثم كيف فعلت هذا ؟ هتاف :)


تحرير:

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

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

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

المحلول

هل من الممكن أن نفترض أن إدراج و حذف الجداول في الزناد ويضمن أن يكون في نفس الأمر ؟

نصائح أخرى

DECLARE @OldKey int, @NewKey int;

SELECT @Oldkey = [ID] FROM DELETED;
SELECT @NewKey = [ID] FROM INSERTED;

هذا يعمل فقط إذا كان لديك صف واحد.وإلا لا يوجد لديك "مرساة" إلى الرابط القديم و الجديد الصفوف.حتى تحقق في الزناد > 1 في المدرج.

أنا لا أعتقد أنه من الممكن.تخيل إذا كان لديك 4 صفوف في الجدول:

1  Val1
2  Val2
3  Val3
4  Val4

الآن مسألة التحديث التالي:

UPDATE MainTable SET
ID = CASE ID WHEN 1 THEN 2 WHEN 2 THEN 1 ELSE ID END
Description = CASE ID WHEN 3 THEN 'Val4' WHEN 4 THEN 'Val3' ELSE Description END

الآن كيف يمكنك التمييز بين ما حدث الصفوف 1 و 2 و ما حدث الصفوف 3 و 4.والأهم من ذلك, هل تستطيع أن تصف ما هو الاختلاف بينهما ؟ كل الأشياء التي يخبرك الأعمدة التي تم تحديثها لن تساعدك.

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

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

ضمن يتسبب في SQL Server لديك حق الوصول إلى جدولين:حذف إدخال.كل من هذه سبق ذكرها.هنا هو كيف تعمل اعتمادا على ما عمل على الزناد وإطلاق النار على:

إدراج العملية

  • حذف - لا تستخدم
  • المدرج - يحتوي على الصفوف الجديدة التي تضاف إلى الجدول

حذف العملية

  • حذف - يحتوي على الصفوف يتم إزالتها من الجدول
  • المدرج - لا تستخدم

عملية تحديث

  • حذف - يحتوي على الصفوف لأنها موجودة قبل عملية التحديث
  • المدرج - يحتوي على الصفوف لأنها موجودة بعد عملية التحديث

هذه الدالة في كل شيء مثل الجداول.ولذلك فمن الممكن تماما أن استخدام صف على أساس عملية مثل الشكل التالي (عملية موجود فقط على مراجعة الجدول ، كما يفعل DateChanged):

INSERT INTO MyAuditTable
(ID, FirstColumn, SecondColumn, ThirdColumn, Operation, DateChanged)
VALUES
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-Before', GETDATE()
FROM deleted
UNION ALL
SELECT ID, FirstColumn, SecondColumn, ThirdColumn, 'Update-After', GETDATE()
FROM inserted

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

ALTER TABLE YourTableName ADD
    PrivateID int NOT NULL IDENTITY (1, 1)
GO

----العمر---- لا من أي وقت مضى تحديث/تغيير القيم الأساسية.كيف يمكنك أن تفعل هذا و إصلاح جميع الأجنبية المفاتيح ؟

لا أنصح أبدا باستخدام الزناد أنه لا يمكن التعامل مع مجموعة من الصفوف.

إذا كان يجب تغيير مفتاح إدراج صف جديد مع حسن مفتاح جديد و قيم استخدام SCOPE_IDENTITY() إذا كان هذا هو ما تفعل الخاص بك.حذف الصف القديم.سجل الصف القديم الذي تم تغييره إلى صف جديد الرئيسية التي يجب أن يكون لديك الآن.آمل أن يكون هناك أي الأجنبية الرئيسية على تغيير المفتاح في السجل الخاص بك...

يمكنك إنشاء هوية جديدة عمود في الجدول MainTable (اسمه على سبيل المثال correlationid) وربط إدراج و حذف الجداول باستخدام هذا العمود.هذا العمود الجديد ينبغي أن تكون شفافة للحصول على التعليمات البرمجية الموجودة.

INSERT INTO LOG(OLDID, NEWID)
SELECT deleted.id AS OLDID, inserted.id AS NEWID
FROM inserted 
INNER JOIN deleted 
    ON inserted.correlationid = deleted.correlationid

إيلاء الاهتمام ، يمكن إدراج سجلات مكررة في سجل الجدول.

بالطبع لا أحد يجب أن يكون تغيير المفتاح الأساسي في الجدول -- ولكن هذا هو بالضبط ما يطلق المفترض أن يكون (في جزء منه) ، هو منع الناس من فعل الأشياء التي لا ينبغي القيام به.انها مهمة تافهة في Oracle أو MySQL لكتابة الزناد التي تعترض تغييرات على المفاتيح الأساسية ويتوقف عليها ، ولكن ليس من السهل على الإطلاق في SQL Server.

ما كنت بالطبع أحب أن تكون قادرة على القيام به سيكون ببساطة تفعل شيئا مثل هذا:

if exists
  (
  select *
    from inserted changed
           join deleted old
   where changed.rowID = old.rowID
     and changed.id != old.id
  )
... [roll it all back]

وهذا هو السبب الناس الخروج غوغلينغ SQL Server ما يعادل ROWID.حسنا, SQL Server لا يكون ذلك ؛ لذا عليك أن تأتي مع نهج آخر.

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

if exists
  (
  select *
    from inserted lost
           left join updated match
             on match.id = lost.id
   where match.id is null
  union
  select *
    from deleted new
           left join inserted match
             on match.id = new.id
    where match.id is null
  )
  -- roll it all back

ولكن هذا لا يزال لا يصاب تحديث مثل...

update myTable
   set id = case
              when id = 1 then 2 
              when id = 2 then 1
              else id
              end

لقد حاولت جعل افتراض أن إدراج و حذف الجداول أمر في مثل هذه الطريقة التي cursoring من خلال إدراج و حذف الجداول في وقت واحد سوف تعطيك بشكل صحيح مطابقة الصفوف.و يظهر هذا العمل.في التأثير الذي بدوره يؤدي إلى ما يعادلها من أجل كل صف المشغلات المتاحة في أوراكل و إلزامية في الخلية...ولكن أتصور الأداء سيكون سيئا على تحديثات ضخمة منذ هذه ليست أصلية السلوك إلى SQL Server.كما أنه يعتمد على افتراض أنني لا يمكن أن تجد في الواقع موثقة في أي مكان و أنا مترددة أن تعتمد على.ولكن قانون منظم بهذه الطريقة يبدو للعمل بشكل صحيح على SQL Server 2008 R2 التثبيت.البرنامج النصي في نهاية هذا المنصب يسلط الضوء على سلوك بسرعة-ولكن-لا-منيع الحل سلوك الثانية الزائفة أوراكل الحل.

إذا كان أي شخص يمكن أن ترشدني إلى مكان افتراضي موثقة ومضمونة من قبل مايكروسوفت سأكون ممتنة جدا الرجل...

begin try
  drop table kpTest;
end try
begin catch
end catch
go

create table kpTest( id int primary key, name nvarchar(10) )
go

begin try
  drop trigger kpTest_ioU;
end try
begin catch
end catch
go

create trigger kpTest_ioU on kpTest
instead of update
as
begin
  if exists
    (
    select *
      from inserted lost
             left join deleted match
               on match.id = lost.id
     where match.id is null
    union
    select *
      from deleted new
             left join inserted match
               on match.id = new.id
      where match.id is null
    )
      raisError( 'Changed primary key', 16, 1 )
  else
    update kpTest
       set name = i.name
      from kpTest
             join inserted i
               on i.id = kpTest.id
    ;
end
go

insert into kpTest( id, name ) values( 0, 'zero' );
insert into kpTest( id, name ) values( 1, 'one' );
insert into kpTest( id, name ) values( 2, 'two' );
insert into kpTest( id, name ) values( 3, 'three' );

select * from kpTest;

/*
0   zero
1   one
2   two
3   three
*/

-- This throws an error, appropriately
update kpTest set id = 5, name = 'FIVE' where id = 1
go

select * from kpTest;

/*
0   zero
1   one
2   two
3   three
*/

-- This allows the change, inappropriately
update kpTest 
   set id = case   
              when id = 1 then 2
              when id = 2 then 1
              else id
              end
     , name = UPPER( name )
go

select * from kpTest

/*
0   ZERO
1   TWO   -- WRONG WRONG WRONG
2   ONE   -- WRONG WRONG WRONG
3   THREE
*/

-- Put it back
update kpTest 
   set id = case   
              when id = 1 then 2
              when id = 2 then 1
              else id
              end
     , name = LOWER( name )
go

select * from kpTest;

/*
0   zero
1   one
2   two
3   three
*/

drop trigger kpTest_ioU
go

create trigger kpTest_ioU on kpTest
instead of update
as
begin
  declare newIDs cursor for select id, name from inserted;
  declare oldIDs cursor for select id from deleted;
  declare @thisOldID int;
  declare @thisNewID int;
  declare @thisNewName nvarchar(10);
  declare @errorFound int;
  set @errorFound = 0;
  open newIDs;
  open oldIDs;
  fetch newIDs into @thisNewID, @thisNewName;
  fetch oldIDs into @thisOldID;
  while @@FETCH_STATUS = 0 and @errorFound = 0
    begin
      if @thisNewID != @thisOldID
        begin
          set @errorFound = 1;
          close newIDs;
          deallocate newIDs;
          close oldIDs;
          deallocate oldIDs;
          raisError( 'Primary key changed', 16, 1 );
        end
      else
        begin
          update kpTest
             set name = @thisNewName
           where id = @thisNewID
          ;
          fetch newIDs into @thisNewID, @thisNewName;
          fetch oldIDs into @thisOldID;
        end
    end;
  if @errorFound = 0
    begin
      close newIDs;
      deallocate newIDs;
      close oldIDs;
      deallocate oldIDs;
    end
end
go

-- Succeeds, appropriately
update kpTest
   set name = UPPER( name )
go

select * from kpTest;

/*
0   ZERO
1   ONE
2   TWO
3   THREE
*/

-- Succeeds, appropriately
update kpTest
   set name = LOWER( name )
go

select * from kpTest;

/*
0   zero
1   one
2   two
3   three
*/


-- Fails, appropriately
update kpTest 
   set id = case   
              when id = 1 then 2
              when id = 2 then 1
              else id
              end
go

select * from kpTest;

/*
0   zero
1   one
2   two
3   three
*/

-- Fails, appropriately
update kpTest 
   set id = id + 1
go

select * from kpTest;

/*
0   zero
1   one
2   two
3   three
*/

-- Succeeds, appropriately
update kpTest 
   set id = id, name = UPPER( name )
go

select * from kpTest;

/*
0   ZERO
1   ONE
2   TWO
3   THREE
*/

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