Bài giảng SQL Server 2 - Thái Ngô Hùng

TRƯỜNG CAO ĐẲNG NGHỀ CƠ ĐIỆN HÀ NỘI  
KHOA CÔNG NGHỆ THÔNG TIN  
BÀI GIẢNG  
SQL SERVER 2  
(Lưu hành nội bộ)  
Biên soạn: Thái Ngô Hùng  
Hà nội, 2012  
CHƯƠNG I THỦ TỤC LƯU TRỮ  
1.1 Thủ tục lưu trữ (stored procedure)  
1.1.1 Cá c khá i niệm  
Như đã đề cập ở các chương trong SQL Server 1, SQL được thiết kế và cài đặt như là  
một ngôn ngữ để thực hiện các thao tác trên cơ sở dữ liệu như tạo lập các cấu trúc  
trong cơ sở dữ liệu, bổ sung, cập nhật, xoá và truy vấn dữ liệu trong cơ sở dữ liệu. Các  
câu lệnh SQL được người sử dụng viết và yêu cầu hệ quản trị cơ sở dữ liệu thực hiện  
theo chế độ tương tác.  
Các câu lệnh SQL có thể được nhúng vào trong các ngôn ngữ lập trình, thông qua đó  
chuỗi các thao tác trên cơ sở dữ liệu được xác định và thực thi nhờ vào các câu lệnh,  
các cấu trúc điều khiển của bản thân ngôn ngữ lập trình được sử dụng.  
Với thủ tục lưu trữ, một phần nào đó khả năng của ngôn ngữ lập trình được đưa vào  
trong ngôn ngữ SQL. Một thủ tục là một đối tượng trong cơ sở dữ liệu bao gồm một  
tập nhiều câu lệnh SQL được nhóm lại với nhau thành một nhóm với những khả năng  
sau:  
• Các cấu trúc điều khiển (IF, WHILE, FOR) có thể được sử dụng trong thủ tục.  
• Bên trong thủ tục lưu trữ có thể sử dụng các biến như trong ngôn ngữ lập trình nhằm  
lưu giữ các giá trị tính toán được, các giá trị được truy xuất được từ cơ sở dữ liệu.  
• Một tập các câu lệnh SQL được kết hợp lại với nhau thành một khối lệnh bên trong  
một thủ tục. Một thủ tục có thể nhận các tham số truyền vào cũng như có thể trả về  
các giá trị thông qua các tham số (như trong các ngôn ngữ lập trình). Khi một thủ  
tục lưu trữ đã được định nghĩa, nó có thể được gọi thông qua tên thủ tục, nhận các  
tham số truyền vào, thực thi các câu lệnh SQL bên trong thủ tục và có thể trả về  
các giá trị sau khi thực hiện xong. Sử dụng các thủ tục lưu trữ trong cơ sở dữ liệu  
sẽ giúp tăng hiệu năng của cơ sở dữ liệu, mang lại các lợi ích sau:  
• Đơn giản hoá các thao tác trên cơ sở dữ liệu nhờ vào khả năng module hoá các  
thao tác này.  
• Thủ tục lưu trữ được phân tích, tối ưu khi tạo ra nên việc thực thi chúng nhanh  
hơn nhiều so với việc phải thực hiện một tập rời rạc các câu lệnh SQL tương  
đương theo cách thông thường.  
• Thủ tục lưu trữ cho phép chúng ta thực hiện cùng một yêu cầu bằng một câu lệnh  
2
đơn giản thay vì phải sử dụng nhiều dòng lệnh SQL. Điều này sẽ làm giảm thiểu  
sự lưu thông trên mạng.  
• Thay vì cấp phát quyền trực tiếp cho người sử dụng trên các câu lệnh SQL và trên  
các đối tượng cơ sở dữ liệu, ta có thể cấp phát quyền cho người sử dụng thông qua  
các thủ tục lưu trữ, nhờ đó tăng khả năng bảo mật đối với hệ thống.  
1.1.2 Tạo thủ tục lưu trữ  
Thủ tục lưu trữ được tạo bởi câu lệnh CREATE PROCEDURE với cú pháp như sau:  
CREATE PROCEDURE tên_thủ_tục [(danh_sách_tham_số)]  
[WITH RECOMPILE|ENCRYPTION|RECOMPILE,ENCRYPTION]  
AS  
Các_câu_lệnh_của_thủ_tục  
Trong đó:  
Tên của thủ tục cần tạo. Tên phải tuân theo qui tắc định danh  
tên_thủ_tục  
và không được vượt quá 128 ký tự.  
Các tham số của thủ tục được khai báo ngay sau tên thủ tục  
và nếu thủ tục có nhiều tham số thì các khai báo phân cách  
nhau bởi dấu phẩy. Khai báo của mỗi một tham số tối thiểu  
danh_sách_tham_số  
phải bao gồm hai phần:  
• Tên tham số được bắt đầu bởi dấu @.  
• Kiểu dữ liệu của tham số  
Ví dụ: @mamonhoc nvarchar(10)  
Thông thường, thủ tục sẽ được phân tích, tối ưu và dịch sẵn  
RECOMPILE  
ở lần gọi đầu tiên. Nếu tuỳ chọn WITH RECOMPILE được  
chỉ định, thủ tục sẽ được dịch lại mỗi khi được gọi  
Thủ tục sẽ được mã hoá nếu tuỳ chọn WITH  
ENCRYPTION được chỉ định. Nếu thủ tục đã được mã hoá,  
ta không thể xem được nội dung của thủ tục.  
ENCRYPTION  
Tập hợp các câu lệnh sử dụng trong nội dung thủ tục. Các  
các_câu_lệnh_của_thủ_tục câu lệnh này có thể đặt trong cặp từ khoá BEGIN...END hoặc  
có thể không.  
3
Ví dụ 1.1: Giả sử ta cần thực hiện một chuỗi các thao tác như sau trên cơ sở dữ liệu  
1. Bổ sung thêm môn học cơ sở dữ liệu có mã TI-005 và số đơn vị học trình là 5 vào  
bảng MONHOC  
2. Lên danh sách nhập điểm thi môn cơ sở dữ liệu cho các sinh viên học lớp có mã  
C24102 (tức là bổ sung thêm vào bảng DIEMTHI các bản ghi với cột MAMONHOC  
nhận giá trị TI-005, cột MASV nhận giá trị lần lượt là mã các sinh viên học lớp có mã  
C24105 và các cột điểm là NULL).  
Nếu thực hiện yêu cầu trên thông qua các câu lệnh SQL như thông thường, ta phải  
thực thi hai câu lệnh như sau:  
INSERT INTO MONHOC  
VALUES('TI-005','Cơ sở dữ liệu',5)  
INSERT INTO DIEMTHI(MAMONHOC,MASV)  
SELECT ‘TI-005’,MASV  
FROM SINHVIEN  
WHERE MALOP='C24102'  
Thay vì phải sử dụng hai câu lệnh như trên, ta có thể định nghĩa môt thủ tục lưu trữ với  
các tham số vào là @mamonhoc, @tenmonhoc, @sodvht và @malop như sau:  
CREATE PROC sp_LenDanhSachDiem(  
@mamonhoc NVARCHAR(10),  
@tenmonhoc NVARCHAR(50),  
@sodvht SMALLINT,  
@malop NVARCHAR(10))  
AS  
BEGIN  
INSERT INTO monhoc  
VALUES(@mamonhoc,@tenmonhoc,@sodvht)  
INSERT INTO diemthi(mamonhoc,masv)  
SELECT @mamonhoc,masv  
FROM sinhvien  
WHERE malop=@malop  
4
END  
Khi thủ tục trên đã được tạo ra, ta có thể thực hiện được hai yêu cầu đặt ra ở trên một  
cách đơn giản thông qua lòi gọi thủ tục:  
sp_LenDanhSachDiem 'TI-005','Cơ sở dữ liệu',5,'C24102'  
1.1.3 Lời gọi thủ tục lưu trữ  
Như đã thấy ở ví dụ ở trên, khi một thủ tục lưu trữ đã được tạo ra, ta có thể yêu cầu hệ  
quản trị cơ sở dữ liệu thực thi thủ tục bằng lời gọi thủ tục có dạng:  
tên_thủ_tục [danh_sách_các_đối_số]  
Số lượng các đối số cũng như thứ tự của chúng phải phù hợp với số lượng và thứ tự  
của các tham số khi định nghĩa thủ tục.  
Trong trường hợp lời gọi thủ tục được thực hiện bên trong một thủ tục khác, bên trong  
một trigger hay kết hợp với các câu lệnh SQL khác, ta sử dụng cú pháp như sau:  
EXECUTE tên_thủ_tục [danh_sách_các_đối_số]  
Thứ tự của các đối số được truyền cho thủ tục có thể không cần phải tuân theo thứ tự  
của các tham số như khi định nghĩa thủ tục nếu tất cả các đối số được viết dưới dạng:  
@tên_tham_số = giá_trị  
Ví dụ 1.2: Lời gọi thủ tục ở ví dụ trên có thể viết như sau:  
sp_LenDanhSachDiem @malop='C24102',  
@tenmonhoc='Cơ sở dữ liệu',  
@mamonhoc='TI-005',  
@sodvht=5  
1.1.4 Sử dụng biến trong thủ tục  
Ngoài những tham số được truyền cho thủ tục, bên trong thủ tục còn có thể sử dụng  
các biến nhằm lưu giữ các giá trị tính toán được hoặc truy xuất được từ cơ sở dữ liệu.  
Các biến trong thủ tục được khai báo bằng từ khoá DECLARE theo cú pháp như sau:  
DECLARE @tên_biến kiểu_dữ_liệu  
Tên biến phải bắt đầu bởi ký tự @ và tuân theo qui tắc về định danh. Ví dụ dưới đây  
minh hoạ việc sử dụng biến trong thủ tục  
Ví dụ 1.3: Trong định nghĩa của thủ tục dưới đây sử dung các biến chứa các giá trị  
truy xuất được từ cơ sở dữ liệu.  
CREATE PROCEDURE sp_Vidu(  
5
@malop1 NVARCHAR(10),  
@malop2 NVARCHAR(10))  
AS  
DECLARE @tenlop1 NVARCHAR(30)  
DECLARE @namnhaphoc1 INT  
DECLARE @tenlop2 NVARCHAR(30)  
DECLARE @namnhaphoc2 INT  
SELECT @tenlop1=tenlop,  
@namnhaphoc1=namnhaphoc  
FROM lop WHERE malop=@malop1  
SELECT @tenlop2=tenlop,  
@namnhaphoc2=namnhaphoc  
FROM lop WHERE malop=@malop2  
PRINT @tenlop1+' nhap hoc nam '+str(@namnhaphoc1)  
print @tenlop2+' nhap hoc nam '+str(@namnhaphoc2)  
IF @namnhaphoc1=@namnhaphoc2  
PRINT 'Hai lớp nhập học cùng năm'  
ELSE  
PRINT 'Hai lớp nhập học khác năm'  
1.1.5 Giá trị trả về của tham số trong thủ tục lưu trữ  
Trong các ví dụ trước, nếu đối số truyền cho thủ tục khi có lời gọi đến thủ tục là biến,  
những thay đổi giá trị của biền trong thủ tục sẽ không được giữ lại khi kết thúc quá  
trình thực hiện thủ tục.  
Ví dụ 1.4: Xét câu lệnh sau đây  
CREATE PROCEDURE sp_Conghaiso(@a INT,@b INT, @c INT)  
AS  
SELECT @c=@a+@b  
Nếu sau khi đã tạo thủ tục với câu lệnh trên, ta thực thi một tập các câu lệnh như sau:  
DECLARE @tong INT  
6
SELECT @tong=0  
EXECUTE sp_Conghaiso 100,200,@tong  
SELECT @tong  
Câu lệnh “SELECT @tong” cuối cùng trong loạt các câu lệnh trên sẽ cho kết quả là: 0  
Trong trường hợp cần phải giữ lại giá trị của đối số sau khi kết thúc thủ tục, ta phải  
khai báo tham số của thủ tục theo cú pháp như sau:  
@tên_tham_số kiểu_dữ_liệu OUTPUT  
hoặc:  
@tên_tham_số kiểu_dữ_liệu OUT  
và trong lời gọi thủ tục, sau đối số được truyền cho thủ tục, ta cũng phải chỉ định thêm  
từ khoá OUTPUT (hoặc OUT)  
Ví dụ 1.5: Ta định nghĩa lại thủ tục ở ví dụ 1.4 như sau:  
CREATE PROCEDURE sp_Conghaiso(  
@a INT,  
@b INT,  
@c INT OUTPUT)  
AS  
SELECT @c=@a+@b  
và thực hiện lời gọi thủ tục trong một tập các câu lệnh như sau:  
DECLARE @tong INT  
SELECT @tong=0  
EXECUTE sp_Conghaiso 100,200,@tong OUTPUT  
SELECT @tong  
thì câu lệnh “SELECT @tong” sẽ cho kết quả là: 300  
1.1.6 Tham số với giá trị mặc định  
Các tham số được khai báo trong thủ tục có thể nhận các giá trị mặc định. Giá trị mặc  
định sẽ được gán cho tham số trong trường hợp không truyền đối số cho tham số khi  
có lời gọi đến thủ tục.  
Tham số với giá trị mặc định được khai báo theo cú pháp như sau:  
@tên_tham_số kiểu_dữ_liệu = giá_trị_mặc_định  
Ví dụ 5.6: Trong câu lệnh dưới đây:  
CREATE PROC sp_TestDefault(  
7
@tenlop NVARCHAR(30)=NULL,  
@noisinh NVARCHAR(100)='Huế')  
AS  
BEGIN  
IF @tenlop IS NULL  
SELECT hodem,ten  
FROM sinhvien INNER JOIN lop  
ON sinhvien.malop=lop.malop  
WHERE noisinh=@noisinh  
ELSE  
SELECT hodem,ten  
FROM sinhvien INNER JOIN lop  
ON sinhvien.malop=lop.malop  
WHERE noisinh=@noisinh AND  
tenlop=@tenlop  
END  
thủ tục sp_TestDefault được định nghĩa với tham số @tenlop có giá trị mặc định là  
NULL và tham số @noisinh có giá trị mặc định là Huế. Với thủ tục được định nghĩa  
như trên, ta có thể thực hiện các lời gọi với các mục đích khác nhau như sau:  
• Cho biết họ tên của các sinh viên sinh tại Huế:  
sp_testdefault  
• Cho biết họ tên của các sinh viên lớp Tin K24 sinh tại Huế:  
sp_testdefault @tenlop='Tin K24'  
• Cho biết họ tên của các sinh viên sinh tại Nghệ An:  
sp_testDefault @noisinh=N'Nghệ An'  
• Cho biết họ tên của các sinh viên lớp Tin K26 sinh tại Đà Nẵng:  
sp_testdefault @tenlop='Tin K26',@noisinh='Đà Nẵng'  
1.1.7 Sửa đổi thủ tục  
Khi một thủ tục đã được tạo ra, ta có thể tiến hành định nghĩa lại thủ tục đó bằng câu  
lệnh ALTER PROCEDURE có cú pháp như sau:  
ALTER PROCEDURE  
tên_thủ_tục [(danh_sách_tham_số)]  
8
[WITH RECOMPILE|ENCRYPTION|RECOMPILE,ENCRYPTION]  
AS  
Các_câu_lệnh_Của_thủ_tục  
Câu lệnh này sử dụng tương tự như câu lệnh CREATE PROCEDURE. Việc sửa đổi  
lại một thủ tục đã có không làm thay đổi đến các quyền đã cấp phát trên thủ tục cũng  
như không tác động đến các thủ tục khác hay trigger phụ thuộc vào thủ tục này.  
1.1.8 Xoá thủ tục  
Để xoá một thủ tục đã có, ta sử dụng câu lệnh DROP PROCEDURE với cú pháp như  
sau:  
DROP PROCEDURE tên_thủ_tục  
Khi xoá một thủ tục, tất cả các quyền đã cấp cho người sử dụng trên thủ tục đó cũng  
đồng thời bị xoá bỏ. Do đó, nếu tạo lại thủ tục, ta phải tiến hành cấp phát lại các quyền  
trên thủ tục đó.  
9
CHƯƠNG II TRIGGER  
Trong chương 1, ta đã biết các ràng buộc được sử dụng để đảm bảo tính toàn vẹn dữ liệu  
trong cơ sở dữ liệu. Một đối tượng khác cũng thường được sử dụng trong các cơ sở dữ  
liệu cũng với mục đích này là các trigger. Cũng tương tự như thủ tục lưu trữ, một trigger  
là một đối tượng chứa một tập các câu lệnh SQL và tập các câu lệnh này sẽ được thực  
thi khi trigger được gọi. Điểm khác biệt giữa thủ tục lưu trữ và trigger là: các thủ tục lưu  
trữ được thực thi khi người sử dụng có lời gọi đến chúng còn các trigger lại được “gọi”  
tự động khi xảy ra những giao tác làm thay đổi dữ liệu trong các bảng.  
Mỗi một trigger được tạo ra và gắn liền với một bảng nào đó trong cơ sở dữ liệu. Khi  
dữ liệu trong bảng bị thay đổi (tức là khi bảng chịu tác động của các câu lệnh INSERT,  
UPDATE hay DELETE) thì trigger sẽ được tự đông kích hoạt.  
Sử dụng trigger một cách hợp lý trong cơ sở dữ liệu sẽ có tác động rất lớn trong việc  
tăng hiệu năng của cơ sở dữ liệu. Các trigger thực sự hữu dụng với những khả năng sau:  
• Một trigger có thể nhận biết, ngăn chặn và huỷ bỏ được những thao tác làm thay  
đổi trái phép dữ liệu trong cơ sở dữ liệu.  
• Các thao tác trên dữ liệu (xoá, cập nhật và bổ sung) có thể được trigger phát hiện  
ra và tự động thực hiện một loạt các thao tác khác trên cơ sở dữ liệu nhằm đảm  
bảo tính hợp lệ của dữ liệu.  
• Thông qua trigger, ta có thể tạo và kiểm tra được những mối quan hệ phức tạp hơn giữa  
các bảng trong cơ sở dữ liệu mà bản thân các ràng buộc không thể thực hiện được.  
2.1 Định nghĩa trigger  
Một trigger là một đối tượng gắn liền với một bảng và được tự động kích hoạt khi xảy  
ra những giao tác làm thay đổi dữ liệu trong bảng. Định nghĩa một trigger bao gồm các  
yếu tố sau:  
• Trigger sẽ được áp dụng đối với bảng nào?  
• Trigger được kích hoạt khi câu lệnh nào được thực thi trên bảng: INSERT,  
UPDATE, DELETE?  
• Trigger sẽ làm gì khi được kích hoạt?  
Câu lệnh CREATE TRIGGER được sử dụng để đinh nghĩa trigger và có cú pháp như sau:  
CREATE TRIGGER  
tên_trigger  
10  
ON tên_bảng  
FOR {[INSERT][,][UPDATE][,][DELETE]}  
AS  
[IF UPDATE(tên_cột)  
[AND UPDATE(tên_cột)|OR UPDATE(tên_cột)]  
...]  
các_câu_lệnh_của_trigger  
Ví dụ 2.1: Ta định nghĩa các bảng như sau:  
Bảng MATHANG lưu trữ dữ liệu về các mặt hàng:  
CREATE TABLE mathang  
(
Mahang NVARCHAR(5) PRIMARY KEY, /*mã hàng*/  
Tenhang NVARCHAR(50) NOT NULL, /*tên hàng*/  
Soluong INT, /*Số lượng hàng hiện có*/  
)
Bảng NHATKYBANHANG lưu trữ thông tin về các lần bán hàng  
CREATE TABLE nhatkybanhang  
(
stt INT IDENTITY PRIMARY KEY,  
ngay DATETIME, /*ngày bán hàng*/  
nguoimua  
NVARCHAR(30), /*tên người mua hàng*/  
mahang NVARCHAR(5)  
FOREIGN KEY REFERENCES mathang(mahang),  
Soluong INT,  
Giaban MONEY  
)
Câu lệnh dưới đây định nghĩa trigger trg_nhatkybanhang_insert. Trigger này có chức  
năng tự động giảm số lượng hàng hiện có khi một mặt hàng nào đó được bán (tức là  
khi câu lệnh INSERT được thực thi trên bảng NHATKYBANHANG).  
CREATE TRIGGER trg_nhatkybanhang_insert  
ON nhatkybanhang  
FOR INSERT  
11  
AS  
UPDATE mathang  
SET mathang.soluong=mathang.soluong-inserted.soluong  
FROM mathang INNER JOIN inserted  
ON mathang.mahang=inserted.mahang  
Với trigger vừa tạo ở trên, nếu dữ liệu trong bảng MATHANG là:  
thì sau khi ta thực hiện câu lênh:  
INSERT INTO nhatkybanhang  
(ngay,nguoimua,mahang,soluong,giaban)  
VALUES('5/5/2004','Tran Ngoc Thanh','H1',10,5200)  
dữ liệu trong bảng MATHANG sẽ như sau:  
Trong câu lệnh CREATE TRIGGER ở ví dụ trên, sau mệnh đề ON là tên của bảng mà  
trigger cần tạo sẽ tác động đến. Mệnh đề tiếp theo chỉ định câu lệnh sẽ kích hoạt  
trigger (FOR INSERT). Ngoài INSERT, ta còn có thể chỉ định UPDATE hoặc  
DELETE cho mệnh đề này, hoặc có thể kết hợp chúng lại với nhau. Phần thân của trigger  
nằm sau từ khoá AS bao gồm các câu lệnh mà trigger sẽ thực thi khi được kích hoạt.  
Chuẩn SQL định nghĩa hai bảng logic INSERTED và DELETED để sử dụng trong  
các trigger. Cấu trúc của hai bảng này tương tự như cấu trúc của bảng mà trigger tác  
động. Dữ liệu trong hai bảng này tuỳ thuộc vào câu lệnh tác động lên bảng làm kích  
hoạt trigger; cụ thể trong các trường hợp sau:  
• Khi câu lệnh DELETE được thực thi trên bảng, các dòng dữ liệu bị xoá sẽ được  
sao chép vào trong bảng DELETED. Bảng INSERTED trong trường hợp này  
không có dữ liệu.  
• Dữ liệu trong bảng INSERTED sẽ là dòng dữ liệu được bổ sung vào bảng gây nên  
sự kích hoạt đối với trigger bằng câu lệnh INSERT. Bảng DELETED trong trường  
hợp này không có dữ liệu.  
• Khi câu lệnh UPDATE được thực thi trên bảng, các dòng dữ liệu cũ chịu sự tác  
động của câu lệnh sẽ được sao chép vào bảng DELETED, còn trong bảng  
12  
INSERTED sẽ là các dòng sau khi đã được cập nhật.  
2.2 Sử dụng mệnh đề IF UPDATE trong trigger  
Thay vì chỉ định một trigger được kích hoạt trên một bảng, ta có thể chỉ định trigger được  
kích hoạt và thực hiện những thao tác cụ thể khi việc thay đổi dữ liệu chỉ liên quan đến  
một số cột nhất định nào đó của cột. Trong trường hợp này, ta sử dụng mệnh đề IF  
UPDATE trong trigger. IF UPDATE không sử dụng được đối với câu lệnh DELETE.  
Ví dụ 2.2: Xét lại ví dụ với hai bảng MATHANG và NHATKYBANHANG, trigger  
dưới đây được kích hoạt khi ta tiến hành cập nhật cột SOLUONG cho một bản ghi của  
bảng NHATKYBANHANG (lưu ý là chỉ cập nhật đúng một bản ghi)  
CREATE TRIGGER trg_nhatkybanhang_update_soluong  
ON nhatkybanhang  
FOR UPDATE  
AS  
IF UPDATE(soluong)  
UPDATE mathang  
SET mathang.soluong = mathang.soluong –  
(inserted.soluong-deleted.soluong)  
FROM (deleted INNER JOIN inserted ON  
deleted.stt = inserted.stt) INNER JOIN mathang  
ON mathang.mahang = deleted.mahang  
Với trigger ở ví dụ trên, câu lệnh:  
UPDATE nhatkybanhang  
SET soluong=soluong+20  
WHERE stt=1  
sẽ kích hoạt trigger ứng với mệnh đề IF UPDATE (soluong) và câu lệnh UPDATE  
trong trigger sẽ được thực thi. Tuy nhiên câu lệnh:  
UPDATE nhatkybanhang  
SET nguoimua='Mai Hữu Toàn'  
WHERE stt=3  
lại không kích hoạt trigger này.  
Mệnh đề IF UPDATE có thể xuất hiện nhiều lần trong phần thân của trigger.  
Khi đó, mệnh đề IF UPDATE nào đúng thì phần câu lệnh của mệnh đề đó sẽ được  
13  
thực thi khi trigger được kích hoạt.  
Ví dụ 2.3: Giả sử ta định nghĩa bảng R như sau:  
CREATE TABLE R  
(
A INT,  
B INT,  
C INT  
)
và trigger trg_R_update cho bảng R:  
CREATE TRIGGER trg_R_test  
ON R  
FOR UPDATE  
AS  
IF UPDATE(A)  
Print 'A updated'  
IF UPDATE(C)  
Print 'C updated'  
Câu lệnh:  
UPDATE R SET A=100 WHERE A=1  
sẽ kích hoạt trigger và cho kết quả là:  
A updated  
và câu lệnh:  
UPDATE R SET C=100 WHERE C=2  
cũng kích hoạt trigger và cho kết quả là:  
C updated  
còn câu lệnh:  
UPDATE R SET B=100 WHERE B=3  
hiển nhiên sẽ không kích hoạt trigger  
2.3 ROLLBACK TRANSACTION và trigger  
Một trigger có khả năng nhận biết được sự thay đổi về mặt dữ liệu trên bảng dữ liệu,  
từ đó có thể phát hiện và huỷ bỏ những thao tác không đảm bảo tính toàn vẹn dữ liệu.  
Trong một trigger, để huỷ bỏ tác dụng của câu lệnh làm kích hoạt trigger, ta sử dụng  
14  
câu lệnh:  
ROLLBACK TRANSACTION  
(Câu lệnh ROLLBACK TRANSACTION sẽ chi tiết ở chương 6)  
Ví dụ 2.4: Nếu trên bảng MATHANG, ta tạo một trigger như sau:  
CREATE TRIGGER trg_mathang_delete  
ON mathang  
FOR DELETE  
AS  
ROLLBACK TRANSACTION  
Thì câu lệnh DELETE sẽ không thể có tác dụng đối với bảng MATHANG. Hay nói  
cách khác, ta không thể xoá được dữ liệu trong bảng.  
Ví dụ 2.5: Trigger dưới đây được kích hoạt khi câu lệnh INSERT được sử dụng để bổ  
sung một bản ghi mới cho bảng NHATKYBANHANG. Trong trigger này kiểm tra  
điều kiện hợp lệ của dữ liệu là số lượng hàng bán ra phải nhỏ hơn hoặc bằng số lượng  
hàng hiện có. Nếu điều kiện này không thoả mãn thì huỷ bỏ thao tác bổ sung dữ liệu.  
CREATE TRIGGER trg_nhatkybanhang_insert  
ON NHATKYBANHANG  
FOR INSERT  
AS  
DECLARE @sl_co int /* Số lượng hàng hiện có */  
DECLARE @sl_ban int  
/* Số lượng hàng được bán */  
DECLARE @mahang nvarchar(5) /* Mã hàng được bán */  
SELECT @mahang=mahang,@sl_ban=soluong  
FROM inserted  
SELECT @sl_co = soluong  
FROM mathang where mahang=@mahang  
/*Nếu số lượng hàng hiện có nhỏ hơn số lượng bán thì huỷ bỏ thao tác bổ sung dữ  
liệu*/  
IF @sl_co<@sl_ban  
ROLLBACK TRANSACTION  
/*Nếu dữ liệu hợp lệ thì giảm số lượng hàng hiện có */  
ELSE  
15  
UPDATE mathang  
SET soluong=soluong-@sl_ban  
WHERE mahang=@mahang  
2.4 Sử dụng trigger trong trường hợp câ u lệnh INSERT, UPDATE và  
DELETE có tác động đến nhiều dò ng dữ liệu  
Trong các ví dụ trước, các trigger chỉ thực sự hoạt động đúng mục đích khi các câu  
lệnh kích hoạt trigger chỉ có tác dụng đối với đúng một dòng dữ liêu. Ta có thể nhận  
thấy là câu lệnh UPDATE và DELETE thường có tác dụng trên nhiều dòng, câu lệnh  
INSERT mặc dù ít rơi vào trường hợp này nhưng không phải là không gặp; đó là khi  
ta sử dụng câu lệnh có dạng INSERT INTO ... SELECT ... Vậy làm thế nào để trigger  
hoạt động đúng trong trường hợp những câu lệnh có tác động lên nhiều dòng dữ liệu?  
Có hai giải pháp có thể sử dụng đối với vấn đề này:  
• Sử dụng truy vấn con.  
• Sử dụng biến con trỏ.  
2.4.1 Sử dụng truy vấn con  
Ta hình dung vấn đề này và cách khắc phục qua ví dụ dưới đây:  
Ví dụ 2.6: Ta xét lại trường hợp của hai bảng MATHANG và NHATKYBANHANG  
như sơ đồ dư
Trigger dưới đây cập nhật lại số lượng hàng của bảng MATHANG khi câu lệnh  
UPDATE được sử dụng để cập nhật cột SOLUONG của bảng NHATKYBANHANG.  
CREATE TRIGGER trg_nhatkybanhang_update_soluong  
ON nhatkybanhang  
FOR UPDATE  
16  
AS  
IF UPDATE(soluong)  
UPDATE mathang  
SET mathang.soluong = mathang.soluong –  
(inserted.soluong-deleted.soluong)  
FROM (deleted INNER JOIN inserted ON  
deleted.stt = inserted.stt) INNER JOIN mathang  
ON mathang.mahang = deleted.mahang  
Với trigger được định nghĩa như trên, nếu thực hiện câu lệnh:  
UPDATE nhatkybanhang  
SET soluong = soluong + 10  
WHERE stt = 1  
thì dữ liệu trong hai bảng MATHANG và NHATKYBANHANG sẽ là:  
Tức là số lượng của mặt hàng có mã H1 đã được giảm đi 10. Nhưng nếu thực hiện tiếp  
câu lệnh:  
UPDATE nhatkybanhang  
SET soluong=soluong + 5  
WHERE mahang='H2'  
dữ liệu trong hai bảng sau khi câu lệnh thực hiện xong sẽ như sau:  
Ta có thể nhận thấy số lượng của mặt hàng có mã H2 còn lại 40 (giảm đi 5) trong khi  
đúng ra phải là 35 (tức là phải giảm 10). Như vậy, trigger ở trên không hoạt động đúng  
trong trường hợp này.  
Để khắc phục lỗi gặp phải như trên, ta định nghĩa lại trigger như sau:  
CREATE TRIGGER trg_nhatkybanhang_update_soluong  
17  
ON nhatkybanhang  
FOR UPDATE  
AS  
IF UPDATE(soluong)  
UPDATE mathang  
SET mathang.soluong = mathang.soluong -  
(SELECT SUM(inserted.soluong-deleted.soluong)  
FROM inserted INNER JOIN deleted  
ON inserted.stt=deleted.stt  
WHERE inserted.mahang = mathang.mahang)  
WHERE mathang.mahang IN (SELECT mahang  
FROM inserted)  
hoặc:  
CREATE TRIGGER trg_nhatkybanhang_update_soluong  
ON nhatkybanhang  
FOR UPDATE  
AS  
IF UPDATE(soluong)  
/*Nếu số lượng dòng được cập nhật bằng 1 */  
IF @@ROWCOUNT = 1  
BEGIN  
UPDATE mathang  
SET mathang.soluong = mathang.soluong –  
(inserted.soluong-deleted.soluong)  
FROM (deleted INNER JOIN inserted ON  
deleted.stt = inserted.stt) INNER JOIN mathang  
ON mathang.mahang = deleted.mahang  
END  
ELSE  
BEGIN  
UPDATE mathang  
SET mathang.soluong = mathang.soluong -  
18  
(SELECT SUM(inserted.soluong-deleted.soluong)  
FROM inserted INNER JOIN deleted  
ON inserted.stt=deleted.stt  
WHERE inserted.mahang = mathang.mahang)  
WHERE mathang.mahang IN (SELECT mahang  
FROM inserted)  
END  
2.4.2 Sử dụng biến con trỏ  
Một cách khác để khắc phục lỗi xảy ra như trong ví dụ 5.17 là sử dụng con trỏ để  
duyệt qua các dòng dữ liệu và kiểm tra trên từng dòng. Tuy nhiên, sử dụng biến con  
trỏ trong trigger là giải pháp nên chọn trong trường hợp thực sự cần thiết.  
Một biến con trỏ được sử dụng để duyệt qua các dòng dữ liệu trong kết quả của một  
truy vấn và được khai báo theo cú pháp như sau:  
DECLARE tên_con_trỏ CURSOR  
FOR câu_lệnh_SELECT  
Trong đó câu lệnh SELECT phải có kết quả dưới dạng bảng. Tức là trong câu lệnh  
không sử dụng mệnh đề COMPUTE và INTO.  
Để mở một biến con trỏ ta sử dụng câu lệnh:  
OPEN tên_con_trỏ  
Để sử dụng biến con trỏ duyệt qua các dòng dữ liệu của truy vấn, ta sử dụng câu lệnh  
FETCH. Giá trị của biến trạng thái @@FETCH_STATUS bằng không nếu chưa duyệt  
hết các dòng trong kết quả truy vấn.  
Câu lệnh FETCH có cú pháp như sau:  
FETCH [[NEXT|PRIOR|FIST|LAST] FROM] tên_con_trỏ  
[INTOdanh_sách_biến ]  
Trong đó các biến trong danh sách biến được sử dụng để chứa các giá trị của các  
trường ứng với dòng dữ liệu mà con trỏ trỏ đến. Số lượng các biến phải bằng với số  
lượng các cột của kết quả truy vấn trong câu lệnh DECLARE CURSOR.  
Ví dụ 2.7: Tập các câu lệnh trong ví dụ dưới đây minh hoạ cách sử dụng biến con trỏ  
để duyệt qua các dòng trong kết quả của câu lệnh SELECT  
DECLARE contro CURSOR  
FOR SELECT mahang,tenhang,soluong FROM mathang  
19  
OPEN contro  
DECLARE @mahang NVARCHAR(10)  
DECLARE @tenhang NVARCHAR(10)  
DECLARE @soluong INT  
/*Bắt đầu duyệt qua các dòng trong kết quả truy vấn*/  
FETCH NEXT FROM contro  
INTO @mahang,@tenhang,@soluong  
WHILE @@FETCH_STATUS=0  
BEGIN  
PRINT 'Ma hang:'+@mahang  
PRINT 'Ten hang:'+@tenhang  
PRINT 'So luong:'+STR(@soluong)  
FETCH NEXT FROM contro  
INTO @mahang,@tenhang,@soluong  
END  
/*Đóng con trỏ và giải phóng vùng nhớ*/  
CLOSE contro  
DEALLOCATE contro  
Ví dụ 2.8: Trigger dưới đây là một cách giải quyết khác của trường hợp được đề cập ở  
ví dụ 2.6  
CREATE TRIGGER trg_nhatkybanhang_update_soluong  
ON nhatkybanhang  
FOR UPDATE  
AS  
IF UPDATE(soluong)  
BEGIN  
DECLARE @mahang NVARCHAR(10)  
DECLARE @soluong INT  
DECLARE contro CURSOR FOR  
SELECT inserted.mahang, inserted.soluong - deleted.soluong AS soluong  
FROM inserted INNER JOIN deleted  
ON inserted.stt=deleted.stt  
20  
Tải về để xem bản đầy đủ
pdf 43 trang yennguyen 23141 Free
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng SQL Server 2 - Thái Ngô Hùng", để tải tài liệu gốc về máy hãy click vào nút Download ở trên

File đính kèm:

  • pdfbai_giang_sql_server_2_thai_ngo_hung.pdf