PDA

View Full Version : نوشتن تریگر برای عمل update و ذخیره در جدول log



sayanpro
یک شنبه 05 بهمن 1393, 19:25 عصر
سلام دوستان گرامی.

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

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

با تشکر.

sajadsobh
دوشنبه 06 بهمن 1393, 00:22 صبح
در زمان آپدیت شدنِ یک جدول می تونی از دو تا جدول 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
دوشنبه 06 بهمن 1393, 00:26 صبح
واسه اینکه زمان تغییر هم ذخیره شه. واسه فیلد تاریخ مقدار پیش فرض رو بذار GETDATE() که تاریخ لحظه آپدیت کردن رو ثبت میکنه.

sayanpro
دوشنبه 06 بهمن 1393, 07:29 صبح
سلام.
این تریگر یک مشکل داره، زمانیکه رکوردهایی که 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
دوشنبه 06 بهمن 1393, 08:49 صبح
سلام.
من خیلی وارد نیستم ولی خب یکمی حرفتون غیر منطقیه ها. شما وقتی تریگر می نویسی به ازای هر آپدیت، یعنی هر یک رکوردی که آپدیت میشه تریگر فراخونی میشه، از نظر شما آپدیت همزمان انجام میشه ولی در واقع رکورد ها تک تک آپدیت میشن.
و اون ارورم فک کنم بخاطر اینکه، به ازای یکی از داده هات چند تا نمره داری، گفته یک مقدار باید بهش پاس بدی نه چند مقدار، مثلا برای علی دو تا نمره پاس بدی در صورتی که یک فضا برای نمره در نظر گرفتی. مشکل این شکلی داری وگرنه تریگر فک نکنم ایرادی داشته باشه.
.
موفق باشی

hamid_hr
دوشنبه 06 بهمن 1393, 10:07 صبح
به ازای هر آپدیت، یعنی هر یک رکوردی که آپدیت میشه تریگر فراخونی میشه،

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

uptade tmp
set
.
.
.
where ID > 10




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

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

صبا صبوحی

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

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

صبا صبوحی

SabaSabouhi
دوشنبه 06 بهمن 1393, 13:17 عصر
سلام
فرمانی که باید استفاده کنید یه چیزی شبیه به این باید بشه.
که باید 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
دوشنبه 06 بهمن 1393, 13:33 عصر
سلام
دوستمون sajadsobh یه اشتباه بزرگ داره. به هیچ عنوان تو Trigger فرض نکنید که فقط یک رکورد داره تغییر می‌کنه.
بنابراین تعریف متغیر تو Trigger خیلی به ندرت می‌تونه معنا داشته باشه.
توی Trigger بسته به این که کارتون Insert, Update, Delete باشه یک یا دو تا جدول فرضی در اختیارتون هست به نام‌های
inserted و deleted. با استفاده از این دو جدول همه کارها رو می‌تونید انجام بدین.
با ثبت رکوردهای جدید فقط جدول insereted در اختیارتون هست که فهرست اقلام اضافه شده به جدول اصلی رو داره.
با ویرایش رکوردها، جدول inserted مقادیر جدید و جدول deleted مقادیر قدیم رو داره.
و با حذف رکوردها، جدول deleted مقادیر سطرهای حذف شده را در اختیار شما قرار می‌دهد.

تذکر مهم: یادتون باشه که هنگام اجرای این Trigger هنوز این رکوردها تو جدول اصلی اعمال نشدن.

صبا صبوحی




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

SabaSabouhi
دوشنبه 06 بهمن 1393, 14:54 عصر
سلام
دوست گرامی، من رو به شک انداختی، تست کردم دیدم حق با شماست.
پست اصلی رو اصلاح می‌کنم.

صبا صبوحی

pnusharp
چهارشنبه 08 بهمن 1393, 00:58 صبح
سلام دوستان!
من یه جدول دارم با رکورد های last-login, reg-date, password, username, name, id
می خوام دوتا تریگر روی این جدول بنویسم! ولی نمیدونم باید چکار کنم!
یک تریگر قبل از تغییر آخرین ورود به سیستم
یکی هم قبل از تغییر یوزرنیم و پسورد
تقاضا دارم راهنمایی کنید...

SabaSabouhi
چهارشنبه 08 بهمن 1393, 11:18 صبح
سلام
نیازی نیست دو تا تریگر بنویسی
همون یک تریگر کافیه، برای این که بدونی کدوم ستون تغییر کرده می‌تونی از ( UPDATE ( MyColumn استفاده کنی.
و با شرط IF و Begin-End برای هر قسمت کنترل‌های خاص رو انجام بدی.

صبا صبوحی

sajaaaaad
چهارشنبه 08 بهمن 1393, 13:52 عصر
من بازم میگم خیلی وارد نیستم، اول جملمم گفتم ولی من هنوز روی حرف خوردم هستم.!
.
شما وقتی میگین

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

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

pnusharp
چهارشنبه 08 بهمن 1393, 22:50 عصر
سلام
نیازی نیست دو تا تریگر بنویسی
همون یک تریگر کافیه، برای این که بدونی کدوم ستون تغییر کرده می‌تونی از ( UPDATE ( MyColumn استفاده کنی.
و با شرط IF و Begin-End برای هر قسمت کنترل‌های خاص رو انجام بدی.

صبا صبوحی

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

pnusharp
پنج شنبه 09 بهمن 1393, 00:32 صبح
دوستان من فردا تا بعدظهر فقط وقت دارم. خواهش می کنم کمک کنید...

SabaSabouhi
پنج شنبه 09 بهمن 1393, 11:32 صبح
من بازم میگم خیلی وارد نیستم، اول جملمم گفتم ولی من هنوز روی حرف خوردم هستم.!
.
شما وقتی میگین

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
پنج شنبه 09 بهمن 1393, 11:39 صبح
دوستان من فردا تا بعدظهر فقط وقت دارم. خواهش می کنم کمک کنید...

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

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

صبا صبوحی

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

pnusharp
پنج شنبه 09 بهمن 1393, 13:38 عصر
سلام. متشکرم سرکار خانم صبوحی!
به قوانین تالار آشنا هستم.
من ابدن جسارت نکردم ک از شما یا دوستان کار بخوام! و اگه گفتم کد، لاأقل یه راهنمایی کد هم ممکن بود منو به جواب برسونه...
در کامنت هام هم نوشتم "کمک"!

sayanpro
پنج شنبه 09 بهمن 1393, 13:40 عصر
سلام من هم با خانم صبوحی خیلی موافقم تا کسی خودش وارد عمل نشه و کدی ننویسه و به مشکل بر نخوره هیچ وقت توکارش پیشرفت نمیکنه. به نظرم آدم باید هی به مشکل بربخوره تا تا بتونه ابزاری که باهاش کار میکنه، یاد بگیره و بهش مسلط بشه.

pnusharp
پنج شنبه 09 بهمن 1393, 14:57 عصر
من برای نوشتن یه تریگر روی جدول 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
پنج شنبه 09 بهمن 1393, 21:05 عصر
وقتی دستور تریگر رو ارسال میکنم این إرور رو میده!!
128023

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

pnusharp
شنبه 11 بهمن 1393, 16:08 عصر
چه بده که آدم انتظار کمک داشته باشه...
و کسانی باشن که بتونن بهش کمک کنن اما کسی این کار رو نکنه...
بخدا این رسمش نیست...:ناراحت::افسرده:

SabaSabouhi
شنبه 11 بهمن 1393, 17:14 عصر
سلام
اگه کسی بتونه کمک کنه مطمئن باش جواب می‌ده.
من تجربه‌ی MySql ندارم و تو پست آخر شما معلومه که از MySql استفاده می‌کنی.

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

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

صبا صبوحی

sajaaaaad
یک شنبه 12 بهمن 1393, 08:53 صبح
سلام نه بابا ناراحتی چیه، البته من اگر دقت کرده باشین توی تمام پستام گفتم که اطلاعاتی ندارم و احتمال میدم این باشه، چون عملاً در کار موردی که اینجئری باشه و چند رکورد باهم آپدیت بشن کم پیش میاد، معمولاً تک تک آپدیت میشه.
.
منون از دوستان که راهنمایی کردن و اگر دوستان کسی در این زمینه تخصص داره راهنمایی کنه ممنون میشم.
لینک مطلب و مشکل من. http://barnamenevis.org/showthread.php?483863-%DA%86%D8%A7%D9%BE-%D8%A8%D8%A7%D8%B1%DA%A9%D8%AF-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%D8%A7%D8%B3%D8%AA%DB%8C%D9%85%D9%88%D9%84-%D9%88-%D9%84%DB%8C%D8%A8%D9%84-%D9%BE%D8%B1%DB%8C%D9%86%D8%AA%D8%B1