لا يرى مشغل SQL Server "بعد الإدراج" الصف الذي تم إدراجه للتو

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

سؤال

النظر في هذا الزناد:

ALTER TRIGGER myTrigger 
   ON someTable 
   AFTER INSERT
AS BEGIN
  DELETE FROM someTable
         WHERE ISNUMERIC(someField) = 1
END

لدي جدول، someTable، وأحاول منع الأشخاص من إدراج سجلات سيئة.لأغراض هذا السؤال، يحتوي السجل السيئ على حقل "someField" وهو رقمي بالكامل.

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

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

هل هذه مشكلة في فهمي للمحفزات؟هل الصفوف المدرجة حديثًا لم يتم الالتزام بها بعد أثناء تشغيل المشغل؟

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

المحلول

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

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

الحل المقبول لذلك هو عدم السماح للمشغل بتغيير البيانات المتغيرة، على الرغم من أنه يمكنه التراجع عن المعاملة.

create table Foo (
       FooID int
      ,SomeField varchar (10)
)
go

create trigger FooInsert
    on Foo after insert as
    begin
        delete inserted
         where isnumeric (SomeField) = 1
    end
go


Msg 286, Level 16, State 1, Procedure FooInsert, Line 5
The logical tables INSERTED and DELETED cannot be updated.

شيء من هذا القبيل سوف يتراجع عن الصفقة.

create table Foo (
       FooID int
      ,SomeField varchar (10)
)
go

create trigger FooInsert
    on Foo for insert as
    if exists (
       select 1
         from inserted 
        where isnumeric (SomeField) = 1) begin
              rollback transaction
    end
go

insert Foo values (1, '1')

Msg 3609, Level 16, State 1, Line 1
The transaction ended in the trigger. The batch has been aborted.

نصائح أخرى

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

CREATE TRIGGER mytrigger ON sometable
INSTEAD OF INSERT
AS BEGIN
  DECLARE @isnum TINYINT;

  SELECT @isnum = ISNUMERIC(somefield) FROM inserted;

  IF (@isnum = 1)
    INSERT INTO sometable SELECT * FROM inserted;
  ELSE
    RAISERROR('somefield must be numeric', 16, 1)
      WITH SETERROR;
END

إذا طلبك لا يريدون لمعالجة الأخطاء (كما يقول جويل هو الحال في التطبيق له)، ثم لا RAISERROR. فقط تأكد الزناد بصمت <م> لا القيام إدراج غير صالح.

وركضت هذا على SQL Server اكسبرس 2005 ويعمل. لاحظ أن يطلق INSTEAD OF <م> لا سبب العودية إذا قمت بإدراج إلى نفس الطاولة التي تعرف على الزناد.

وهنا لي نسخة معدلة من قانون بيل:

CREATE TRIGGER mytrigger ON sometable
INSTEAD OF INSERT
AS BEGIN
  INSERT INTO sometable SELECT * FROM inserted WHERE ISNUMERIC(somefield) = 1 FROM inserted;
  INSERT INTO sometableRejects SELECT * FROM inserted WHERE ISNUMERIC(somefield) = 0 FROM inserted;
END

وهذا يتيح إدراج تنجح دائما، وأي سجلات وهمية الحصول على القيت في sometableRejects حيث يمكنك التعامل معها في وقت لاحق. من المهم أن يصبح لديك ترفض مجالات استخدام الجدول NVARCHAR في كل شيء - لا [إينتس]، tinyints، الخ -. لأنهم إذا كنت تحصل على رفض، انها لأن البيانات غير ما هو متوقع لها أن تكون

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

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

واعتقد انه يمكن استخدام التحقق من القيد - هو بالضبط ما اخترع ذلك ل

.
ALTER TABLE someTable 
ADD CONSTRAINT someField_check CHECK (ISNUMERIC(someField) = 1) ;

وجوابي السابقة (أيضا الحق قد يكون مبالغة بعض الشيء):

وأعتقد أن الطريق الصحيح هو استخدام بدلا من الزناد لمنع بيانات خاطئة من اندراجها (بدلا من حذفها بعد فوات الأوان)

وUPDATE: حذف من مشغل يعمل على كلا MSSQL 7 و MSSQL 2008

وأنا لا جورو العلائقية، ولا افتة معايير SQL. ومع ذلك - خلافا للإجابة مقبولة - صفقات MSSQL على ما يرام مع كل ص <وأ href = "http://msdn.microsoft.com/en-us/library/aa258254(SQL.80).aspx" يختلط = "noreferrer" > متداخلة تقييم الزناد ecursive و. أنا لا أعرف عن RDBMSs آخرين.

والخيارات ذات الصلة هي 'مشغلات العودية "و" المشغلات متداخلة ". تقتصر مشغلات المتداخلة إلى 32 المستويات، والافتراضية إلى 1. مشغلات العودية وإيقاف افتراضيا، وليس هناك حديث عن الحد - ولكن بصراحة، لم يسبق لي ان حولتهم، لذلك أنا لا أعرف ما يحدث مع ما لا مفر منه تجاوز المكدس. وأظن أن MSSQL مجرد قتل سبيد (أو هناك حد عودي).

وبطبيعة الحال، أن يظهر فقط أن الإجابة المقبولة لديها سبب خاطئ <م> ، وليس أنه غير صحيح. ومع ذلك، قبل بدلا من المشغلات، وأذكر كتابة ON INSERT يطلق التي سوف تقوم بتحديث بمرح الصفوف المدرجة فقط. هذا وعملت كل شيء بخير، وكما هو متوقع.

اختبار سريع من DELETEing الصف المدرج فقط يعمل أيضا:

 CREATE TABLE Test ( Id int IDENTITY(1,1), Column1 varchar(10) )
 GO

 CREATE TRIGGER trTest ON Test 
 FOR INSERT 
 AS
    SET NOCOUNT ON
    DELETE FROM Test WHERE Column1 = 'ABCDEF'
 GO

 INSERT INTO Test (Column1) VALUES ('ABCDEF')
 --SCOPE_IDENTITY() should be the same, but doesn't exist in SQL 7
 PRINT @@IDENTITY --Will print 1. Run it again, and it'll print 2, 3, etc.
 GO

 SELECT * FROM Test --No rows
 GO

لديك شيء آخر يجري هنا.

من إنشاء الزناد توثيق:

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

وهذا يمنحك على الأقل طريقة لرؤية البيانات الجديدة.

لا أستطيع رؤية أي شيء في المستندات يحدد أنك لن ترى البيانات المدرجة عند الاستعلام عن الجدول العادي بالرغم من ذلك...

ولقد وجدت هذا المرجع:

create trigger myTrigger
on SomeTable
for insert 
as 
if (select count(*) 
    from SomeTable, inserted 
    where IsNumeric(SomeField) = 1) <> 0
/* Cancel the insert and print a message.*/
  begin
    rollback transaction 
    print "You can't do that!"  
  end  
/* Otherwise, allow it. */
else
  print "Added successfully."

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

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

هل من الممكن أن INSERT صحيح، ولكن أن UPDATE منفصل ويتم بعد ذلك غير صالحة لكنها لن اطلاق النار على الزناد؟

والتقنيات المذكورة أعلاه تصف خياراتك جيدا جدا. ولكن ما هي المستخدمين رؤية؟ لا أستطيع أن أتخيل كيف يمكن لصراع الأساسية مثل هذا بينك وبين من هو المسؤول عن البرنامج لا يمكن أن ينتهي في البلبلة والعداء مع المستخدمين.

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

.

وتحرير:

وسوف يسجل أول "الحذف" وأعترف إلى نشر ما سبق عندما ظهرت هذا السؤال الأول. I تخاذل بالطبع خارجا عندما رأيت أنه كان من جويل سبولسكي. ولكن يبدو أنه سقط في مكان ما قرب. لا تحتاج الأصوات، ولكن سوف أضع على السجل.

وIME، وموجبات وذلك نادرا ما الإجابة الصحيحة عن أي شيء آخر من قيود التكامل غرامة الحبيبات خارج نطاق قواعد العمل.

وMS-SQL يحتوي على إعداد لمنع اطلاق الزناد متكررة. وconfirgured هذا عن طريق sp_configure تخزين proceedure، حيث يمكنك تشغيل مشغلات العودية أو متداخلة أو إيقاف تشغيله.

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

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

ورمز المشغل الخاص بك التي يمكن أن تستخدم في هذه الطريقة ستكون كما يلي:

ALTER TRIGGER myTrigger
    ON someTable
    AFTER INSERT
AS BEGIN
DELETE FROM someTable
    INNER JOIN inserted on inserted.primarykey = someTable.primarykey
    WHERE ISNUMERIC(inserted.someField) = 1
END

والخاص بك "الزناد" تفعل شيئا أن "الزناد" لا يفترض أن تفعل. يمكنك بسيطة يكون لديك SQL Server عامل تشغيل

DELETE FROM someTable
WHERE ISNUMERIC(someField) = 1

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

لقد عثرت على هذا السؤال أثناء البحث عن تفاصيل حول تسلسل الأحداث أثناء عبارة الإدراج والمشغل.انتهى بي الأمر بترميز بعض الاختبارات الموجزة للتأكد من كيفية عمل SQL 2016 (EXPRESS) - واعتقدت أنه سيكون من المناسب مشاركتها لأنها قد تساعد الآخرين في البحث عن معلومات مماثلة.

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

على سبيل المثال - بافتراض أن لدينا جدول "جدول" يحتوي على حقل عدد صحيح "حقل" وحقل مفتاح أساسي "pk" والكود التالي في مشغل الإدراج الخاص بنا:

select @value=field,@pk=pk from inserted
update table set field=@value+1 where pk=@pk
waitfor delay '00:00:15'

نقوم بإدخال صف بالقيمة 1 لـ "الحقل"، ثم سينتهي الصف بالقيمة 2.علاوة على ذلك - إذا قمت بفتح نافذة أخرى في SSMS وحاولت:حدد * من الجدول حيث pk = @pk

حيث @pk هو المفتاح الأساسي الذي قمت بإدراجه في الأصل، سيكون الاستعلام فارغًا حتى تنتهي مدة 15 ثانية وسيظهر بعد ذلك القيمة المحدثة (الحقل = 2).

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

select @value=field,@pk=pk from inserted
update table set field=@value+1 where pk=@pk
delete from table where pk=@pk
waitfor delay '00:00:15'

مرة أخرى، استغرق تنفيذ الإدراج 15 ثانية.لم يُظهر الاستعلام الذي تم تنفيذه في جلسة مختلفة أي بيانات جديدة - أثناء أو بعد تنفيذ عملية الإدراج + المشغل (على الرغم من أنني أتوقع زيادة أي هوية حتى لو لم يتم إدراج أي بيانات).

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