# پایگاه‌های داده > SQL Server > T-SQL >  نوشتن تریگر برای عمل update و ذخیره در جدول log

## sayanpro

سلام دوستان گرامی.

فرض کنید که یک جدول به نام tb1 و با فیلدهای id,name,family دارید می خواهید، زمانی که عمل update روی این جدول انجام شد تریگر بیاد تغییرات پیش آمده + اطلاعات قبل از تغییر را در یک جدول log که دارای فیلدهای oldName,newName,oldFamily,newFamily,updateDate است تغییرات ثبت کنه. فرض کنید تغییرات به روز رسانی می تواند بر روی یک یا چند رکورد انجام شود.

ممنون می شوم راهنمایی فرمائید چگونه این تریگر را بنویسم که اگر تغییرات بر روی چند رکورد در جدول tb1 اتفاق افتاد همه تغییرات یاد شده + اطلاعات قبل از تغییر و زمان تغییر در جدول Log ذخیره شود.

با تشکر.

----------


## sajadsobh

در زمان آپدیت شدنِ یک جدول می تونی از دو تا جدول inserted و deleted استفاده کنی. اطلاعاتی که تازه توی یک جدول درج میشه، میره توی جدول inserted میشینه و اطلاعاتی که قبلاً توی اون جدول بوده و شما با مقادیر جدید آپدیت کردی، میره توی جدول deleted
در واقع میخواد بگه که عمل آپدیت چیزی جز پاک شدنِ اطلاعات قبلی و درج اطلاعات جدید نیست!
من جدول اصلی رو d1 و جدول دوم که میخوای تغییرات انجام شده رو نشون بدی رو d2 در نظر گرفتم. جدول اصلی هم دو تا فیلد بیشتر نداره اونم name و score
تو اینو واسه جدول خودت بسطش بده:

CREATE TRIGGER tr_upd 
   ON  d1
   AFTER UPDATE
AS 
BEGIN

DECLARE @old_name NVARCHAR(50)
DECLARE @new_name NVARCHAR(50)
DECLARE @old_score INT
DECLARE @new_score INT

SELECT @old_name = name FROM deleted
SELECT @old_score = score FROM deleted
SELECT @new_name = name FROM inserted
SELECT @new_score = score FROM inserted

INSERT INTO d2 (old_name, new_name, old_score, new_score)
VALUES (@old_name, @new_name, @old_score, @new_score)
 
END

----------


## sajadsobh

واسه اینکه زمان تغییر هم ذخیره شه. واسه فیلد تاریخ مقدار پیش فرض رو بذار GETDATE() که تاریخ لحظه آپدیت کردن رو ثبت میکنه.

----------


## sayanpro

سلام.
این تریگر یک مشکل داره، زمانیکه رکوردهایی که update می شوند چندتا باشند، پیغام خطای زیر نمایش می دهد.Msg 512, Level 16, State 1, Procedure updateTrigger, Line 12
Subquery returned more than 1 value. This is not permitted when the subquery follows =, !=, <, <= , >, >= or when the subquery is used as an expression.
The statement has been terminated.می تونید خودتون هم تست کنید.

----------


## sajaaaaad

سلام.
من خیلی وارد نیستم ولی خب یکمی حرفتون غیر منطقیه ها. شما وقتی تریگر می نویسی به ازای هر آپدیت، یعنی هر یک رکوردی که آپدیت میشه تریگر فراخونی میشه، از نظر شما آپدیت همزمان انجام میشه ولی در واقع رکورد ها تک تک آپدیت میشن.
و اون ارورم فک کنم بخاطر اینکه، به ازای یکی از داده هات چند تا نمره داری، گفته یک مقدار باید بهش پاس بدی نه چند مقدار، مثلا برای علی دو تا نمره پاس بدی در صورتی که یک فضا برای نمره در نظر گرفتی. مشکل این شکلی داری وگرنه تریگر فک نکنم ایرادی داشته باشه.
.
موفق باشی

----------


## hamid_hr

> به ازای هر آپدیت، یعنی هر یک رکوردی که آپدیت میشه تریگر فراخونی میشه،


نه ببینین مثلا شما میزنین

uptade tmp
set 
.
.
.
where ID > 10




در این صورت شاید چندین رکورد آپدیت بشن ولی فقط یک بار تریگر اجرا میشه

----------


## SabaSabouhi

سلام
دوستمون sajadsobh یه اشتباه بزرگ داره. به هیچ عنوان تو Trigger فرض نکنید که فقط یک رکورد داره تغییر می‌کنه.
بنابراین تعریف متغیر تو Trigger خیلی به ندرت می‌تونه معنا داشته باشه.
توی Trigger بسته به این که کارتون Insert, Update, Delete باشه یک یا دو تا جدول فرضی در اختیارتون هست به نام‌های
inserted و deleted. با استفاده از این دو جدول همه کارها رو می‌تونید انجام بدین.
با ثبت رکوردهای جدید فقط جدول insereted در اختیارتون هست که فهرست اقلام اضافه شده به جدول اصلی رو داره.
با ویرایش رکوردها، جدول inserted مقادیر جدید و جدول deleted  مقادیر قدیم رو داره.
و با حذف رکوردها، جدول deleted مقادیر سطرهای حذف شده را در اختیار شما قرار می‌دهد.

صبا صبوحی

----------


## SabaSabouhi

> سلام.
> من خیلی وارد نیستم ولی خب یکمی حرفتون غیر منطقیه ها. شما وقتی تریگر می نویسی به ازای هر آپدیت، یعنی هر یک رکوردی که آپدیت میشه تریگر فراخونی میشه، از نظر شما آپدیت همزمان انجام میشه ولی در واقع رکورد ها تک تک آپدیت میشن.
> و اون ارورم فک کنم بخاطر اینکه، به ازای یکی از داده هات چند تا نمره داری، گفته یک مقدار باید بهش پاس بدی نه چند مقدار، مثلا برای علی دو تا نمره پاس بدی در صورتی که یک فضا برای نمره در نظر گرفتی. مشکل این شکلی داری وگرنه تریگر فک نکنم ایرادی داشته باشه.
> .
> موفق باشی


سلام
بر خلاف نظر شما حرف دوستمون کاملاً منطقی هست. برخی فرمان‌ها گروهی عمل می‌کنن.
مثل DELETE FROM MyTable که تمام رکوردها رو حذف می‌کنه 
یا UPDATE MyTable SET Value = Value + 1
یا INSERT INTO MyTable SELECT * FROM YourTable

صبا صبوحی

----------


## SabaSabouhi

سلام
فرمانی که باید استفاده کنید یه چیزی شبیه به این باید بشه.
که باید OldValues و d.values رو با فیلدهایی که می‌خواین ثبت کنی، جایگزین کنی.

INSERT INTO Log
    SELECT OldValues = d.values, NewValues = i.values
    FROM Inserted i
    JOIN Deleted d on d.Id = i.Id


صبا صبوحی

----------


## sajadsobh

> سلام
> دوستمون sajadsobh یه اشتباه بزرگ داره. به هیچ عنوان تو Trigger فرض نکنید که فقط یک رکورد داره تغییر می‌کنه.
> بنابراین تعریف متغیر تو Trigger خیلی به ندرت می‌تونه معنا داشته باشه.
> توی Trigger بسته به این که کارتون Insert, Update, Delete باشه یک یا دو تا جدول فرضی در اختیارتون هست به نام‌های
> inserted و deleted. با استفاده از این دو جدول همه کارها رو می‌تونید انجام بدین.
> با ثبت رکوردهای جدید فقط جدول insereted در اختیارتون هست که فهرست اقلام اضافه شده به جدول اصلی رو داره.
> با ویرایش رکوردها، جدول inserted مقادیر جدید و جدول deleted  مقادیر قدیم رو داره.
> و با حذف رکوردها، جدول deleted مقادیر سطرهای حذف شده را در اختیار شما قرار می‌دهد.
> 
> ...





آره حرف شما کاملاً منطقیه ولی با اون تذکر آخرتون من یکم با شما موافق نیستم. مگه توی تعریف تریگر ما از After Update استفاده نمیکنیم؟ خب وقتی تغییرات اعمال میشه این تریگر اجرا میشه دیگه. اینجوری نیست مگه؟ یا نه من اشتباه میکنم باز؟

----------


## SabaSabouhi

سلام
دوست گرامی، من رو به شک انداختی، تست کردم دیدم حق با شماست.
پست اصلی رو اصلاح می‌کنم.

صبا صبوحی

----------


## pnusharp

سلام دوستان!
من یه جدول دارم با رکورد های last-login, reg-date, password, username, name, id
می خوام دوتا تریگر روی این جدول بنویسم! ولی نمیدونم باید چکار کنم!
یک تریگر قبل از تغییر آخرین ورود به سیستم
یکی هم قبل از تغییر یوزرنیم و پسورد
تقاضا دارم راهنمایی کنید...

----------


## SabaSabouhi

سلام
نیازی نیست دو تا تریگر بنویسی
همون یک تریگر کافیه، برای این که بدونی کدوم ستون تغییر کرده می‌تونی از (  UPDATE ( MyColumn  استفاده کنی.
و با شرط IF و Begin-End برای هر قسمت کنترل‌های خاص رو انجام بدی.

صبا صبوحی

----------


## sajaaaaad

من بازم میگم خیلی وارد نیستم، اول جملمم گفتم ولی من هنوز روی حرف خوردم هستم.!
.
شما وقتی میگین
UPDATE MyTable SET Value = Value + 1
درسته به نظر شما چندتا رکورد هم زمان اپدیت میشه ولی هر رکوردی که آپدیت میشه یکبارم تریگر اجرا میشه.! البته فک کنم.! مثلا شما ده تا رکورد داری که به مقدار Value هر کدوم 1 واحد قراره اضافه بشه، وقتی رکورد اول آپدیت میشه به ازاش تریگر فراخوانی میشه، وقتی رکورد دوم آپدیت میشه تریگر دوباره فراخوانی میشه تا ده تا رکووورد.
.
من تست نکردم این موردو ولی بعید میدونم نرم افزار به این قوی یی، اینقد خنگ عمل کنه و نفهمه چندتا رکوورد آپدیت شده.

----------


## sayanpro

دوست خوبم آقا sajaaaad می تونید تست کنید. فرض کنید که فیلدی که قرار آپدیت بشه تکراری تو جدول. زمانیکه عمل به روز رسانی روی یک رکورد اتفاق بیافته مشکلی نیست و تریگر درست کار میکنه ولی زمانیکه چندتا رکورد به روز میشن این پیغام خطا میده  :افسرده:

----------


## pnusharp

> سلام
> نیازی نیست دو تا تریگر بنویسی
> همون یک تریگر کافیه، برای این که بدونی کدوم ستون تغییر کرده می‌تونی از (  UPDATE ( MyColumn  استفاده کنی.
> و با شرط IF و Begin-End برای هر قسمت کنترل‌های خاص رو انجام بدی.
> 
> صبا صبوحی


سلام. متشکرم 
آخه استادمون گفته که باید 2 تریگر بنویسید!!
کدش رو اگه کسی از دوستان برام بزاره ممنون میشم. من چند بار کدش رو در محیط SQL، در My SQL نوشتم اما إرور میداد!
حالا الزامن نیاز نیست که تریگر ها همون ها باشه که در پست اول عرض کردم!
هر کجا شما احساس میکنید که دو تا تریگر میشه نوشت، کافیه! :ناراحت:

----------


## pnusharp

دوستان من فردا تا بعدظهر فقط وقت دارم. خواهش می کنم کمک کنید...

----------


## SabaSabouhi

> من بازم میگم خیلی وارد نیستم، اول جملمم گفتم ولی من هنوز روی حرف خوردم هستم.!
> .
> شما وقتی میگین
> UPDATE MyTable SET Value = Value + 1
> درسته به نظر شما چندتا رکورد هم زمان اپدیت میشه ولی هر رکوردی که آپدیت میشه یکبارم تریگر اجرا میشه.! البته فک کنم.! مثلا شما ده تا رکورد داری که به مقدار Value هر کدوم 1 واحد قراره اضافه بشه، وقتی رکورد اول آپدیت میشه به ازاش تریگر فراخوانی میشه، وقتی رکورد دوم آپدیت میشه تریگر دوباره فراخوانی میشه تا ده تا رکووورد.
> .
> من تست نکردم این موردو ولی بعید میدونم نرم افزار به این قوی یی، اینقد خنگ عمل کنه و نفهمه چندتا رکوورد آپدیت شده.


سلام
دوست عزیز، از این پست من دلخور نشی، فقط یک انتقاد دوستانه هست.
چرا وقتی اطلاعات کافی نداری، نظر اشتباه خودت رو به این قاطعیت ابراز می‌کنی و ازش دفاع می‌کنی.
تست کردن این مورد خیلی ساده هست، خوبه یک بار تست کنی بعد با این قاطعیت اظهار نظر کنی.

قطعاً با این فرمانی که شما نوشتین، یک و فقط یک بار Trigger فعال می‌شه و اون ده تا رکورد تو جدول inserted قرار داره.

و باز هم دلیلی نداره که اگه یک نرم‌افزار معتبر مثل Sql Server درست مثل انتظار شما رفتار نکنه، «خنگ» باشه.
شما سعی کن قبل از این که دیگران رو متهم کنی، اول کمی به دانسته‌های خودت شک کنی، شاید شما هم اشتباه کنی.
( راستی جهت اطلاع دوستان خنگ به معنی اسب سفید هست )

1. یک دیتابیس بساز
2. یک جدول بساز با یک کلید Identity و یک ستون عددی به نام Test
3. یک جدول بساز با یک کلید Identity و یک ستون متن به نام Log
4. برای جدول اول یک Trigger بنویس به این صورت 

CREATE TRIGGER [dbo].[TrgTest]
   ON [dbo].[Test]
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN		
	DECLARE @Text as varchar(50)
	SELECT @Text = 'Trigger Activated for ' + CONVERT( varchar(20), Count(*) ) + ' items'
		FROM inserted
	INSERT INTO Log VALUES( @Text )
END




5. چند تا رکورد اضافه کن به جدول Test و مقدار جدول Log رو ببین، 
در این مرحله همیشه یکی یکی انجام می‌شه
6. اون فرمان UPDATE Test SET Value = Value + 1 رو اجرا کن
بعد دوباره مقدار جدول Log رو ببین.

به همین راحتی می‌تونستی قبل از تاکید روی نظر اشتباه، مساله رو تست کنی.

صبا صبوحی

----------


## SabaSabouhi

> دوستان من فردا تا بعدظهر فقط وقت دارم. خواهش می کنم کمک کنید...


سلام 
دوست من، تا جایی که من می‌دونم این انجمن «حل‌المسائل» نیست. یعنی قرار نیست دوستان سفارش
برنامه‌نویسی یا script نویسی بدن و جواب بگیرن.
شما Trigger خودت رو بنویس و بگذار اینجا. دوستان زیادی هستن که اشکال کارت رو بهت می‌گن. خود من هم
اگه دیگران جواب مناسب و کافی نداده باشن و نسبت به مشکل شما دانش کافی هم داشته باشم، پاسخ می‌دم.
اینطوری هم مشکلت حل می‌شه و هم درست انجام دادنش رو یاد می‌گیری.

از نظر من، رفع اشکال دوستان، فقط یک کمک هست که بالا بردن تجربه و تخصص دوستان و همکاران. 
ولی اگر قرار باشه کاری رو به شکل خاص برای شما انجام بدم، دیگه می‌شه «کار» و شما می‌شی «کارفرما»
 و من بابت «کار»، از «کارفرما» انتظار «دستمزد» دارم.
گمان می‌کنم سایر حرفه‌ای‌های این انجمن هم با من موافق باشن.

صبا صبوحی

پانوشت: یک ضرب‌المثل ژاپنی می‌که «اگه از کسی بدت میاد بهش ماهی بده، اگه دوستش داره بهش قلاب بده
و اگه خیلی خیلی دوستش داری، بهش قلاب ساختن رو یاد بده»

----------


## pnusharp

سلام. متشکرم سرکار خانم صبوحی!
به قوانین تالار آشنا هستم.
من ابدن جسارت نکردم ک از شما یا دوستان کار بخوام! و اگه گفتم کد، لاأقل یه راهنمایی کد هم ممکن بود منو به جواب برسونه...
در کامنت هام هم نوشتم "کمک"!

----------


## sayanpro

سلام من هم با خانم صبوحی خیلی موافقم تا کسی خودش وارد عمل نشه و کدی ننویسه و به مشکل بر نخوره هیچ وقت توکارش پیشرفت نمیکنه. به نظرم آدم باید هی به مشکل بربخوره تا تا بتونه ابزاری که باهاش کار میکنه، یاد بگیره و بهش مسلط بشه.

----------


## pnusharp

من برای نوشتن یه تریگر روی جدول tc_user با فیلدهای  last-login, reg-date, password, username, name, id
باد ابتدا یه جدول بسازم برای حفظ تغییراتی که روی جدول tc_user ایجاد شده؟ً! درسته؟! مثلن جدول tc_user_audit  به صورت زیر

 CREATE TABLE tc_user_audit ( 
    id int(11) NOT NULL AUTO_INCREMENT,
     changedon datetime DEFAULT NULL,
    last_login varchar(255) NOT NULL,
     action varchar(50) DEFAULT NULL,
    PRIMARY KEY (id)  )


بعد از ایجاد این جدول تریگر رو به اینصورت بنویسم:


CREATE TRIGGER before_user_update
     BEFORE UPDATE ON tc_user
    FOR EACH ROW BEGIN 
    INSERT INTO tc_user_audit 
   SET action = 'update', 
    id = OLD.id,  
      last_login = OLD.last-login,   
     changedon = NOW(); END

----------


## pnusharp

وقتی دستور تریگر رو ارسال میکنم این إرور رو میده!!
Captue.JPG

کسی نیست به من کمک کنه؟؟؟!  :ناراحت:

----------


## pnusharp

چه بده که آدم انتظار کمک داشته باشه...
و کسانی باشن که بتونن بهش کمک کنن اما کسی این کار رو نکنه...
بخدا این رسمش نیست... :ناراحت:  :افسرده:

----------


## SabaSabouhi

سلام
اگه کسی بتونه کمک کنه مطمئن باش جواب می‌ده.
من تجربه‌ی MySql ندارم و تو پست آخر شما معلومه که از MySql استفاده می‌کنی.

اما یه چیز خاصی تو این تصویر دیدم، کلمه BEFORE مشکی نوشته شده و ظاهراً به عبارت قبلی که نام Trigger هست
چسبیده، یه فاصله اونجا بده ببین مشکل حل می‌شه یا نه. ( بعضی وقت‌ها مشکلات به همین سادگی حل می‌شن  :لبخند:  )
اگه هم نشد، سعی اول محل مشکل رو پیدا کنی.
یعنی اول Trigger رو خالی بساز ببین ساخته می‌شه یا نه.
بعد تکه به تکه کد رو اضافه کن.

مشکل Syntax معمولاً حل کردنش خیلی ساده هست.

صبا صبوحی

----------


## sajaaaaad

سلام نه بابا ناراحتی چیه، البته من اگر دقت کرده باشین توی تمام پستام گفتم که اطلاعاتی ندارم و احتمال میدم این باشه، چون عملاً در کار موردی که اینجئری باشه و چند رکورد باهم آپدیت بشن کم پیش میاد، معمولاً تک تک آپدیت میشه.
.
منون از دوستان که راهنمایی کردن و اگر دوستان کسی در این زمینه تخصص داره راهنمایی کنه ممنون میشم.
لینک مطلب و مشکل من. https://barnamenevis.org/showthread.p...86%D8%AA%D8%B1

----------

