2.2. MÔ HÌNH TỔNG QUÁT VÀ CÁC TRIGGER TRONG ORACLE
2.2.1. Mô hình tổng quát của CSDL tích cực
Mô hình đƣợc sử dụng để chỉ rõ các quy tắc của cơ sở dữ liệu tích cực đƣợc tham chiếu đến nhƣ là mô hình ECA (Event-Condition-Active). Một quy tắc trong mô hình ECA có 3 thành phần:
a. Sự kiện (Event) làm kích hoạt quy tắc: các sự kiện này thường là các thao tác cập nhật cơ sở dữ liệu được áp dụng một cách tường minh đối với cơ sở dữ liệu. Tuy nhiên, trong mô hình tổng quát chúng cũng có thể là các sự kiện thời gian hoặc là các dạng sự kiện ngoài khác.
b. Điều kiện (Condition) xác định hành động của quy tắc có thể đƣợc thực hiện hay không: mỗi khi sự kiện kích hoạt có mặt, một điều kiện đƣợc chọn
có thể đƣợc tính giá trị. Nếu không có điều kiện nào đƣợc chỉ rõ, hành động sẽ đƣợc thực hiện sự kiện xảy ra. Nếu điều kiện đƣợc chỉ rõ, đầu tiên nó đƣợc tính giá trị và chỉ khi nó tính giá trị là đúng (true) thì hành động của quy tắc sẽ đƣợc thực hiện.
c. Hành động (Action) thực hiện: Hành động thường là một dãy lệnh SQL nhưng nó cũng có thể là một giao tác cơ sở dữ liệu hoặc một chương trình bên ngoài sẽ đƣợc thực hiện một cách tự động.
Hãy xem một vài ví dụ minh họa khái niệm này. Có hai quan hệ NHÂN VIÊN và ĐƠN VỊ. Mỗi nhân viên có một tên (tenNV), mã số (masoNV), lương, mã số đơn vị (masoDV) là khóa ngoài tham chiếu đến DONVI, và mã số người giám sát (MasoNGS) là khóa ngoài đệ quy đến NHANVIEN. Với ví dụ này, chúng ta giả thiết rằng masoNV có thể cho phép có giá trị null, chỉ ra rằng nhân viên có thể tạm thời chƣa đƣợc đăng ký vào đơn vị nào. Mỗi đơn vị có một tên (TenDV), một mã số (MasoDV), tổng lương của tất cả các nhân viên đăng ký vào đơn vị (Tongluong) và một người quản lý (MasoNQL) là khóa ngoài đến NHANVIEN.
Chú ý rằng thuôc tính Tongluong thực chất là một thuộc tính suy diễn đƣợc, giá trị của nó là tổng lương của tất cả các nhân viên đăng ký vào một đơn vị cụ thể. Việc duy trì giá trị đúng của một thuộc tính suy diễn đƣợc nhƣ vậy có thể được thực hiện thông qua một luật tích cực. Trước tiên chúng ta phải xác định các sự kiện có thể gây ra một thay đổi giá trị của Tongluong, đó là các sự kiện sau:
[1]. Chèn vào một hoặc nhiều bộ giá trị nhân viên mới [2]. Thay đổi lương của một hoặc nhiều nhân viên có sẵn
[3]. Thay đổi việc đăng ký của các nhân viên có sẵn từ đơn vị này sang đơn vị khác.
[4]. Loại bỏ một hoặc nhiều bộ giá trị nhân viên.
Trong trường hợp sự kiện 1, chúng ta chỉ cần tính lại Tongluong nếu nhân viên mới đƣợc ghi tức khắc vào một đơn vị - nghĩa là giá trị của thuộc tính MasoNV đối với bộ nhân viên mới là khác null (giả thiết null là cho phép đối
với MasoNV). Như vậy điều đó sẽ là điều kiện để kiểm tra. Một điều kiện tương tự có thể sẽ đƣợc kiểm tra cho sự kiện 2 (và 4) để xác định xem có phải là nhân viên mà lương của anh ta bị thay đổi (hoặc bị xóa) hiện tại đã được đăng ký vào một đơn vị hay không. Với sự kiện 3, chúng ta luôn luôn thực hiện một hành động để duy trì giá trị của Tongluong một cách đúng đắn, nhƣ vậy là không cần điều kiện nào (hành động luôn luôn đƣợc thực hiện).
Hành động đối với các sự kiện 1, 2 và 4 là cập nhật một cách tự động giá trị của Tongluong đối với đơn vị của nhân viên để phản ánh việc lương của nhân viên vừa mới được thêm, xóa hoặc cập nhật. Trong trường hợp của sự kiện 3, cần một hành động đúp: một để cập nhật Tongluong của đơn vị cũ của nhân viên và hành động khác để cập nhật Tongluong của đơn vị mới của nhân viên.
Bốn active rules R1, R2, R3 và R4 tương ứng với tình trạng ở trên có thể đƣợc chỉ ra trong ký hiệu của hệ quản trị cơ sở dữ liệu Oracle. Chúng ta hãy xét quy tắc R1 để minh họa cú pháp của việc tạo ra các luật tích cực trong Oracle.
Lệnh CREATE TRIGGER chỉ rõ tên của một trigger (hoặc các luật tích cực) – Tongluong1 cho R1. Mệnh đề AFTER chỉ ra rằng quy tắc sẽ đƣợc kích hoạt sau sự kiện kích hoạt quy tắc xảy ra. Các sự kiện kích hoạt – ví dụ nhƣ chèn một nhân viên mới ở trong ví dụ này – đƣợc chỉ ra sau từ khóa AFTER. Mệnh đề ON chỉ rõ quan hệ mà trên đó quy tắc đƣợc chỉ ra – NHÂN VIÊN đối với R1. Các từ khóa tùy chọn FOR EACH ROW chỉ ra rằng quy tắc sẽ đƣợc kích hoạt một lần với mỗi một hàng bị ảnh hưởng bởi sự kiện kích hoạt. Mệnh đề tùy chọn WHEN đƣợc sử dụng để chỉ ra điều kiện nào cần kiểm tra sau khi quy tắc đƣợc kích hoạt nhưng trước khi hành động được thực hiện. Cuối cùng, hành động cần làm được chỉ ra như một khối PL/SQL, khối này thường chứa một hoặc nhiều lệnh SQL hoặc các lời gọi để thực hiện các thủ tục bên ngoài.
R1: CREATE TRIGGER Tongluong1 AFTER INSERT ON NHÂNVIÊN FOR EACH ROW
WHEN (NEW.MasoDV IS NOT NULL) UPDATE ĐƠNVỊ
SET Tongluong = Tongluong + NEW.Luong WHERE MasoDV = NEW.MasoDV;
R2: CREATE TRIGGER Tongluong2
AFTER UPDATE OF Luong ON NHÂNVIÊN FOR EACH ROW
WHEN (NEW.MasoDV IS NOT NULL) UPDATE ĐƠNVỊ
SET Tongluong = Tongluong + NEW.Luong – OLD.Luong WHERE MasoDV = NEW.MasoDV;
R3: CREATE TRIGGER Tongluong3
AFTER UPDATE OF DNO ON NHÂNVIÊN FOR EACH ROW
BEGIN
UPDATE ĐƠNVỊ
SET Tongluong = Tongluong + NEW.Luong WHERE MasoDV = NEW.MasoDV;
UPDATE ĐƠNVỊ
SET Tongluong = Tongluong - OLD.Luong WHERE MasoDV = OLD.MasoDV;
END;
R4: CREATE TRIGGER Tongluong4 AFTER DELETE ON NHÂNVIÊN FOR EACH ROW
WHEN (OLD.MasoDV IS NOT NULL) UPDATE ĐƠNVỊ
SET Tongluong = Tongluong - OLD.Luong WHERE MasoDV = OLD.MasoDV;
Bốn trigger (các quy tắc tích cực) R1, R2, R3 và R4 minh họa một số tính chất của các quy tắc tích cực. Trước tiên, các sự kiện cơ bản có thể chỉ ra để kích hoạt các quy tắc là các lệnh cập nhật của SQL chuẩn: INSERT, DELETE,
UPDATE. Chúng đƣợc chỉ ra bằng các từ khóa INSERT, DELETE, UPDATE trong ký hiệu của Oracle. Trong trường hợp của UPDATE người ta có thể chỉ ra các thuộc tính đƣợc cập nhật – ví dụ, bằng cách viết UPDATE OF Luong, MasoDV. Thứ hai, người thiết kế quy tắc cần có cách tham chiếu đến các bộ giá trị đã đƣợc chèn, xóa, sửa đổi: Các từ khóa NEW và OLD đƣợc sử dụng trong Oracle: NEW đƣợc sử dụng để tham chiếu đến bộ vừa đƣợc chèn vào hoặc vừa đƣợc sửa đổi, trong khi đó OLD đƣợc sử dụng để tham chiếu đến bộ bị xóa hoặc bộ trước khi được cập nhật.
Nhƣ vậy, quy tắc R1 đƣợc kích hoạt sau một phép toán INSERT đƣợc áp dụng cho quan hệ NHÂNVIÊN. Trong R1, điều kiện (NEW.MasoDV IS NOT NULL) đƣợc kiểm tra, và nếu nó đƣợc tính giá trị là đúng, nghĩa là bộ nhân viên vừa mới đƣợc chèn vào là có quan hệ với một đơn vị, thì hành động sẽ đƣợc thực hiện. Hành động cập nhật các bộ ĐƠNVỊ có liên quan tới nhân viên vừa mới được chèn vào bằng cách cộng lương của người đó (NEW.Luong) vào thuộc tính Tongluong của đơn vị liên quan của chúng.
Quy tắc R2 tương tự với R1 nhưng nó được kích hoạt bằng một phép toán UPDATE, sửa đổi lương của một nhân viên thay vì chèn. Quy tắc R3 được kích hoạt bằng một sửa đổi đối với thuộc tính MasoDV của NHÂNVIÊN, nó có nghĩa thay đổi đăng ký của nhân viên từ một đơn vị sang một đơn vị khác.
Không có điều kiện để kiểm tra trong R3, vì vậy hành động đƣợc thực hiện mỗi khi sự kiện kích hoạt xuất hiện. Hành động sửa đổi cả đơn vị mới và đơn vị cũ của nhân viên đăng ký lại bằng cách cộng lương của họ vào Tongluong của đơn vị mới và trừ lương của họ ra khỏi Tongluong của đơn vị cũ. Để ý rằng điều này có thể làm việc ngay cả giá trị của MasoDV là NULL bởi vì trong trường hợp này không đơn vị nào đƣợc lựa chọn cho hành động của quy tắc.
Điều quan trọng là xét ảnh hưởng của mệnh đề tùy chọn FOR EACH ROW, nó có nghĩa là quy tắc đƣợc kích hoạt một cách riêng rẽ đối với mỗi bộ giá trị. Điều này đƣợc biết đến nhƣ một row-level-trigger. Nếu mệnh đề này bị bỏ qua, trigger sẽ đƣợc biết nhƣ là một statement-level trigger và sẽ đƣợc kích hoạt một lần đối với mỗi lệnh kích hoạt. Để thấy sự khác nhau, hãy xem phép
toán cập nhật sau đây, nó tăng 10% lương cho tất cả các nhân viên đăng ký vào đơn vị 5. Phép toán này sẽ là một sự kiện kích hoạt quy tắc R2:
R2: UPDATE NHÂNVIÊN SET Luong = 1.1* Luong WHERE MasoDV=5;
Bởi vì lệnh ở trên có thể cập nhật nhiều bản ghi, một quy tắc sử dụng ngôn ngữ row-level nhƣ là R2 sẽ đƣợc kích hoạt một lần đối với mỗi hàng, trong khi đó một quy tắc sử dụng ngữ nghĩa statement-level đƣợc kích hoạt chỉ một lần.
Hệ thống Oracle cho phép người sử dụng chọn lựa một trong hai tùy chọn trên để sử dụng cho mỗi quy tắc. Việc dựa vào mệnh đề tùy chọn FOR EACH ROW tạo ra một row-level trigger và việc bỏ nó tạo ra một statement-level trigger. Để ý rằng các từ khóa NEW và OLD chỉ có thể đƣợc sử dụng với các row-level trigger.
Cú pháp để chỉ ra các trigger trong hệ thống Oracle đƣợc tổng kết nhƣ sau:
<trigger>:: = CREATE TRIGGER <tên Trigger>
(AFTER| BEFORE) <sự kiện kích hoạt> ON <tên bảng>
[ FOR EACH ROW]
[WHEN <điều kiện>]
<hành động của trigger>;
<Sự kiện kích hoạt>::= <sự kiện của trigger>
<Sự kiện của trigger>::= <INSERT| DELETE| UPDATE| [OF <tên cột>
{, <tên cột>}].
Một ví dụ khác, giả sử rằng chúng ta muốn kiểm tra có phải lương của một nhân viên là lớn hơn lương của người giám sát trực tiếp của anh ta hay không?
Nhiều sự kiện có thể kích hoạt quy tắc này: việc chèn vào một nhân viên mới, thay đổi lương của một nhân viên hoặc thay đổi người giám sát của nhân viên.
Giả sử rằng hành động sẽ thực hiện sẽ là một lời gọi đến một thủ tục bên ngoài INFORM_SUPERVISOR, nó sẽ thông báo về người giám sát. Quy tắc có thể đƣợc viết nhƣ sau:
R5: CREATE TRIGGER INFORM_SUPERVISOR1
BEFORE INSERT OF UPDATE OF Luong, MasoNGS ON NHÂNVIÊN FOR EACH ROW
WHEN
(NEW.Luong > (SELECT Luong FROM NHÂNVIÊN WHERE MasoNV = NEW.MasoNGS))
INFORM_SUPERVISOR (NEW.MasoNGS, NEW.MasoNV);