سلام و دو صد بدرود
چند وقت پیش دوستی در مورد ثبت رویداد های کاربران و کلا کنترل عملکرد کاربران ازم پرسید و نتیجه گفتگو و تحقیقاتمون اینی شد که میخوام بگم .
کلا بحث با این شروع میشه که چطور بفهمیم کدوم کاربر چه کاری رو از کدوم سیستم با کدوم شناسه در چه زمانی انجام داده .
این عملیات رو میشه به دو صورت انجام داد : یکی از طریق لایه دیتابیس که وقتی برای هر عملی احضار میشه بعد از انجام اون عمل یک گزارش از عملکرد رو روی یه جدول خاص (حالا شاید روی یه دیتابیس دیگه) ثبت کنه و یه راه دیگه استفاده از تریگر هاست .
راه اول هزینه بر هست و و کم انعطاف پس وقتمون رو میزاریم رو روش دوم
تریگر
کلا ترگیر چیه ؟
نقل قول: تريگر نوع خاصي از روال ذخيره شده است و در هنگام تغيير داده اي كه براي حفاظت از آن طراحي شده است فعال مي گردد. تريگرها با جلوگيري از تغييرات غير معتبر يا ناسازگار در داده ها به حفظ يكپارچگي داده اي كمك مي كنند. فرض كنيد دو جدول customer و orders در اختيار داريد بنابراين مي توانيد تريگري ايجاد كنيد كه با ايجاد هر سفارش جديد، اعتبار شماره مشتري آن را كنترل كند. همچنين مي توانيد تريگري ديگري ايجاد كنيد كه در صورت حذف يك مشتري جدول orders را كنترل كرده و در صورت وجود سفارش براي آن مشتري فرآيند حذف مشتري را متوقف سازد.
تريگرها مي توانند قواعد كاري پيچيده تري را نسبت به قيود اعمال كنند. براي مثال يك تريگر insert مي تواند در هنگام درج ركورد جديد به سفارشات فعال شود و وضعيت پرداخت مشتري را بررسي و نوع پرداخت مناسب وي را تعيين كند.
تريگرها پارامتر ندارند و صريحا فعال نمي شوند و براي فعال سازي آنها تغييرات داده اي لازم است.
تريگرها را مي توان تا 32 سطح تودرتو تعريف كرد. اين تريگرها به شكل زير عمل مي كنند:
براي مثال تريگر جدول orders يك ورودي به جدول حسابهاي دريافتي اضافه مي كند و اين ورودي به نوبه خود تريگر ديگري را براي بررسي وضعيت مشتري فعال مي نمايد. يك تريگر به روز رساني جدول را انجام مي ده و همين امر تريگر ديگري را فعال مي كند.
بطور پيش فرض تمامي تريگرها (UPDATE,DELETE,INSERT)پس از تغييرات داده اي فعال مي شوند و به تريگر AFTER معروف مي باشند. در ويرايش هاي قبلي SQL Server تنها اين نوع تريگر موجود بوده است. در SQL Server 2000 نوع ديگري به نام INSTEAD OF معرفي شده است كه بجاي تغييرات داده اي موردنظر فعال مي شود.
از نقطه نظر كارائي، تريگرها سربار زيادي ندارند. بيشتر زمان اجراي يك تريگر براي ارجاع به ساير جدول بكار مي رود. اين ارجاع در صورت وجود جداول در حافظه سريع و در صورت نيار به خواندن از ديسك كمي كندتر خواهد بود.
تريگرها بخشي از يك تراكنش (transaction) محسوب مي شوند. اگر تريگر يا هر بخشي از تراكنش با شكست مواجه شود كل تراكنش عقبگرد مي شود.
منبع : computer-tech.blogfa.com
خلاصه مطلب بالا میشه اینکه وقتی روی جدولی 3 عمل اصلی Insert – Update – Delete اتفاق افتاد یه روالی شروع بکار بکند و این روال توانایی رد هر عملی انجام گرفته را دارد .
تریگر در سه سطح قابل پیاده سازی هست
1- سطح اینستنس
2- سطح دیتابیس
3- سطح جدول یا دیدگاه
که از پایین به بالا میریم
ساختار تولید تریگر بصورت زیر است
کد:
Trigger on an INSERT, UPDATE, or DELETE statement to a table or view (DML Trigger)
CREATE TRIGGER [ schema_name . ]trigger_name
ON { table | view }
[ WITH <dml_trigger_option> [ ,...n ] ]
{ FOR | AFTER | INSTEAD OF }
{ [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
[ NOT FOR REPLICATION ]
AS { sql_statement [ ; ] [ ,...n ] | EXTERNAL NAME <method specifier [ ; ] > }
<dml_trigger_option> ::=
[ ENCRYPTION ]
[ EXECUTE AS Clause ]
<method_specifier> ::=
assembly_name.class_name.method_name
توضیحاتش :
کد:
CREATE TRIGGER trigger_name
کلمه کلیدی ساخت تریگر و نام اون
روی کدوم جدول و یا دیدگاه ساخته بشه
اختیاری : کد بشه - قابل ویرایش و کپی نباشه
کد:
FOR | AFTER | INSTEAD OF
قبل از- بعد از- بجای
کد:
INSERT ,UPDATE,DELETE
اینم که مشخصه
کد:
[ NOT FOR REPLICATION ]
اختیاری: این تریگر برای ستونی که مقدار not for replication اون فعال هست اجرا نشه
برای اطلاعات بیشتر در مورد
not for replication
چند تا مثال :
در جداولی به ساختار زیر اطلاعات مشتری ، اطلاعات کالا هاو اطلاعات سفارشات وجود دارد :
کد:
CREATE TABLE [dbo].[customers](
[Id] [int] IDENTITY(1,1) NOT NULL,
[NameFamily] [nvarchar](50) NULL,
[Credit] [money] NULL,
CONSTRAINT [PK_customers] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Goods](
[Id] [int] IDENTITY(1,1) NOT NULL,
[GoodName] [nvarchar](50) NULL,
[GoodCount] [int] NULL,
[GoodFee] [money] NULL,
CONSTRAINT [PK_Goods] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
CREATE TABLE [dbo].[Orders](
[Id] [int] IDENTITY(1,1) NOT NULL,
[CID] [int] NULL,
[GID] [int] NULL,
[GCount] [int] NULL,
[GSUM] [money] NULL CONSTRAINT [DF_Orders_GSUM] DEFAULT ((0))
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_customers] FOREIGN KEY([CID])
REFERENCES [dbo].[customers] ([Id])
GO
ALTER TABLE [dbo].[Orders] CHECK CONSTRAINT [FK_Orders_customers]
GO
ALTER TABLE [dbo].[Orders] WITH CHECK ADD CONSTRAINT [FK_Orders_Goods] FOREIGN KEY([GID])
REFERENCES [dbo].[Goods] ([Id])
GO
ALTER TABLE [dbo].[Orders] CHECK CONSTRAINT [FK_Orders_Goods]
حالا تریگری که قبل از ثبت سفارش موجودی انبار و اعتبار مشتری رو چک میکنه و اگر کم بود اجازه ثبت نمیده :
کد:
CREATE TRIGGER [dbo].[checkBalance]
ON [dbo].[Orders]
FOR insert
AS
BEGIN
IF (SELECT goodcount FROM goods WHERE id=(SELECT gid FROM Inserted)) < (SELECT gcount FROM Inserted)
BEGIN
RAISERROR ('موجودي کالا کم است',10,1)
rollback
END
IF (SELECT credit FROM customers WHERE id=(SELECT cid FROM Inserted)) <
((SELECT gcount FROM Inserted)*(SELECT goodFee FROM goods WHERE id=(SELECT gid FROM Inserted)))
BEGIN
RAISERROR ('موجودي حساب مشتری کم است',10,1)
rollback
END
SET NOCOUNT ON;
END
تریگری که بعد از ثبت سفارش از موجودی انبار و اعتبار مشتری به میزان خرید کم میکنه و بعد از حذف سفارش مقادیر بازگشتی رو در جداول کالا و مشتری بر میگردونه
کد:
CREATE TRIGGER [dbo].[setSum]
ON [dbo].[Orders]
after INSERT,delete
AS
BEGIN
SET NOCOUNT ON;
IF exists(SELECT * FROM inserted)
BEGIN
UPDATE orders SET gsum =((SELECT gcount FROM Inserted)*(SELECT goodFee FROM goods WHERE id=(SELECT gid FROM Inserted)))
WHERE id=@@IDENTITY
UPDATE customers SET credit=credit-(select gsum FROM orders WHERE id=@@IDENTITY)WHERE id=(SELECT cid FROM inserted)
UPDATE Goods SET Goodcount =goodcount-(select gcount FROM orders WHERE id=@@IDENTITY)WHERE id=(SELECT gid FROM inserted)
END
ELSE
BEGIN
UPDATE customers SET credit=creadit+(select gsum FROM deleted)WHERE id=(SELECT cid FROM deleted)
UPDATE Goods SET Goodcount =goodcount+(select gcount FROM deleted )WHERE id=(SELECT gid FROM deleted)
END
END
و یا تریگری که بجای حذف اطلاعات مشتری موجودی اونو منفی میکنه :
کد:
CREATE TRIGGER [dbo].[insteadDelete]
ON [dbo].[customers]
instead OF delete
AS
BEGIN
IF (SELECT credit FROM deleted) >0
UPDATE customers SET credit=credit *-1 WHERE id=(SELECT id FROM deleted)
SET NOCOUNT ON;
END
توضیح : با استفاده از RAISERROR میتونیم خطائی رو به کاربر و یا سطح بالاتر برگردونیم باعث تولید EVENT میشه که بعدا توضیح میدم و ساختار کلیش برابر است با :
کد:
RAISERROR ( { msg_id | msg_str | @local_variable }
{ ,severity ,state }
[ ,argument [ ,...n ] ] )
[ WITH option [ ,...n ] ]
حالا یه جدول داریم با ساختار زیر :
کد:
CREATE TABLE [dbo].[logs](
[db_table] [nvarchar](50) NULL,
[db_command] [nvarchar](50) NULL,
[db_commandtext] [nvarchar](MAX) NULL,
[db_username] [nvarchar](50) NULL CONSTRAINT [DF_logs_db_username] DEFAULT (suser_sname()),
[db_domain] [nvarchar](50) NULL CONSTRAINT [DF_logs_db_domain] DEFAULT (user_name()),
[db_datetime] [nvarchar](50) NULL CONSTRAINT [DF_logs_db_datetime] DEFAULT (getdate()),
[db_app] [nvarchar](50) NULL CONSTRAINT [DF_logs_db_app] DEFAULT (app_name())
) ON [PRIMARY]
میخواهییم وقتی اطلاعات هر کدوم از جدول های مشتری-کالا و سفارشات تغییر کرد(حذف-اضافه-بروز رسانی) در این جدول گزارشش ثبت بشه :
پس به هر کدوم از جدول ها این تریگر رو اضافه میکنیم :
کد:
CREATE TRIGGER [dbo].[LogIt]
ON [dbo].[customers]
AFTER INSERT,DELETE,UPDATE
AS
BEGIN
DECLARE @NewData nvarchar(MAX);
DECLARE @Event varchar (20);
IF (EXISTS(SELECT * FROM INSERTED) AND EXISTS(SELECT * FROM DELETED))
BEGIN
SET @NewData='id='+ cast((SELECT TOP 1 id FROM DELETED)AS varchar)+',NameFamily='+ (SELECT TOP 1 NameFamily FROM DELETED)+',Credit='+ cast((SELECT TOP 1 Credit FROM DELETED)AS varchar);
SET @Event='UPDATE'
END
else IF EXISTS(SELECT * FROM INSERTED)
BEGIN
SET @NewData='id='+ cast((SELECT TOP 1 id FROM inserted)AS varchar)+',NameFamily='+ (SELECT TOP 1 NameFamily FROM inserted)+',Credit='+ cast((SELECT TOP 1 Credit FROM inserted)AS varchar);
SET @Event='INSERT'
END
else IF EXISTS(SELECT * FROM DELETED)
BEGIN
SET @NewData='id='+ cast((SELECT TOP 1 id FROM DELETED)AS varchar)+',NameFamily='+ (SELECT TOP 1 NameFamily FROM DELETED)+',Credit='+ cast((SELECT TOP 1 Credit FROM DELETED)AS varchar);
SET @Event='DELETE'
END
SET NOCOUNT ON;
INSERT INTO logs (db_table,db_command,db_commandtext)VALUES ('customers',@Event,@NewData)
END
نکته : در جدول مشتری ها اگر ردیفی را پاک کنیم گزارش آن بصورت آپدیت ثبت خواهد شد چرا که به جای حذف دستور آپدیت رو فراخوانی کردیم و همچنین اگر شخصی که حسابش منفی هست یعنی قبلاپاک شده رو مجدد پاک کنیم هیچ گزارشی ثبت نخواهد شد چرا که هیچ حذف و یا بروز رسانی اتفاق نمی افتاد
توابعی وجود داره که براساس ارتباط فعلی مقدار دهی شدن
بعضی از کارآمدترین ها اینها هستن :
کد:
Suser_name نام کاربری ویندوز کاربر
User_name نام کاربری که با ان به سرور وصل شدیم
Getdate تاریخ سرور
App_name نام برنامه ای که در ارتباط با سرور است
@@Rowcount تعداد رکورد هایی که در آخرین عمل سلکت انتخاب شدند
Db_name() نام دیتابیسی که در حال استفاده است
@@IDENTITY آخرین مقدار فیلد خود افزایشی
نکته : اگر توجه کنین کنین تو جدول گزارشات فقط 3 ستون رو مقدار دهی میکنیم و بقیه 4 تا ستون با مقادیر پیش فرض تکمیل می شن که این مقادیر با کمک توابع سیستمی اس کیو ال مقدار دهی شدن .
این مطلب ادامه دارد