Câu trả lời:
Những gì bạn cần là hai kích hoạt để nắm bắt tình trạng tuổi không hợp lệ
Sau đây là dựa trên phương pháp bẫy lỗi được xử lý bằng jerry cho Trình kích hoạt MySQL từ Chương 11, Trang 254-256 của cuốn sách Lập trình thủ tục lưu trữ MySQL dưới tiêu đề 'Xác thực dữ liệu với trình kích hoạt' :
drop table mytable;
create table mytable (
id smallint unsigned AUTO_INCREMENT,
age tinyint not null,
primary key(id)
);
DELIMITER $$
CREATE TRIGGER checkage_bi BEFORE INSERT ON mytable FOR EACH ROW
BEGIN
DECLARE dummy,baddata INT;
SET baddata = 0;
IF NEW.age > 20 THEN
SET baddata = 1;
END IF;
IF NEW.age < 1 THEN
SET baddata = 1;
END IF;
IF baddata = 1 THEN
SELECT CONCAT('Cannot Insert This Because Age ',NEW.age,' is Invalid')
INTO dummy FROM information_schema.tables;
END IF;
END; $$
CREATE TRIGGER checkage_bu BEFORE UPDATE ON mytable FOR EACH ROW
BEGIN
DECLARE dummy,baddata INT;
SET baddata = 0;
IF NEW.age > 20 THEN
SET baddata = 1;
END IF;
IF NEW.age < 1 THEN
SET baddata = 1;
END IF;
IF baddata = 1 THEN
SELECT CONCAT('Cannot Update This Because Age ',NEW.age,' is Invalid')
INTO dummy FROM information_schema.tables;
END IF;
END; $$
DELIMITER ;
insert into mytable (age) values (10);
insert into mytable (age) values (15);
insert into mytable (age) values (20);
insert into mytable (age) values (25);
insert into mytable (age) values (35);
select * from mytable;
insert into mytable (age) values (5);
select * from mytable;
Đây là kết quả:
mysql> drop table mytable;
Query OK, 0 rows affected (0.03 sec)
mysql> create table mytable (
-> id smallint unsigned AUTO_INCREMENT,
-> age tinyint not null,
-> primary key(id)
-> );
Query OK, 0 rows affected (0.06 sec)
mysql> DELIMITER $$
mysql> CREATE TRIGGER checkage_bi BEFORE INSERT ON mytable FOR EACH ROW
-> BEGIN
-> DECLARE dummy,baddata INT;
-> SET baddata = 0;
-> IF NEW.age > 20 THEN
-> SET baddata = 1;
-> END IF;
-> IF NEW.age < 1 THEN
-> SET baddata = 1;
-> END IF;
-> IF baddata = 1 THEN
-> SELECT CONCAT('Cannot Insert This Because Age ',NEW.age,' is Invalid')
-> INTO dummy FROM information_schema.tables;
-> END IF;
-> END; $$
Query OK, 0 rows affected (0.08 sec)
mysql> CREATE TRIGGER checkage_bu BEFORE UPDATE ON mytable FOR EACH ROW
-> BEGIN
-> DECLARE dummy,baddata INT;
-> SET baddata = 0;
-> IF NEW.age > 20 THEN
-> SET baddata = 1;
-> END IF;
-> IF NEW.age < 1 THEN
-> SET baddata = 1;
-> END IF;
-> IF baddata = 1 THEN
-> SELECT CONCAT('Cannot Update This Because Age ',NEW.age,' is Invalid')
-> INTO dummy FROM information_schema.tables;
-> END IF;
-> END; $$
Query OK, 0 rows affected (0.07 sec)
mysql> DELIMITER ;
mysql> insert into mytable (age) values (10);
Query OK, 1 row affected (0.06 sec)
mysql> insert into mytable (age) values (15);
Query OK, 1 row affected (0.05 sec)
mysql> insert into mytable (age) values (20);
Query OK, 1 row affected (0.04 sec)
mysql> insert into mytable (age) values (25);
ERROR 1172 (42000): Result consisted of more than one row
mysql> insert into mytable (age) values (35);
ERROR 1172 (42000): Result consisted of more than one row
mysql> select * from mytable;
+----+-----+
| id | age |
+----+-----+
| 1 | 10 |
| 2 | 15 |
| 3 | 20 |
+----+-----+
3 rows in set (0.00 sec)
mysql> insert into mytable (age) values (5);
Query OK, 1 row affected (0.07 sec)
mysql> select * from mytable;
+----+-----+
| id | age |
+----+-----+
| 1 | 10 |
| 2 | 15 |
| 3 | 20 |
| 4 | 5 |
+----+-----+
4 rows in set (0.00 sec)
mysql>
Cũng xin lưu ý rằng các giá trị gia tăng tự động không bị lãng phí hoặc mất.
Hãy thử một lần !!!
Các ràng buộc KIỂM TRA không được thực hiện trong MySQL. Từ TẠO BẢNG
Mệnh đề CHECK được phân tích cú pháp nhưng bị bỏ qua bởi tất cả các công cụ lưu trữ. Xem Phần 12.1.17, Syntax BẢNG TẠO Cú pháp. Lý do chấp nhận nhưng bỏ qua các mệnh đề cú pháp là để tương thích, để dễ dàng chuyển mã từ các máy chủ SQL khác và để chạy các ứng dụng tạo bảng có tham chiếu. Xem Phần 1.8.5, Sự khác biệt của MySQL MySQL từ SQL SQL tiêu chuẩn.
Bên cạnh giải pháp kích hoạt tốt của @Rolando, còn có một cách giải quyết khác về vấn đề này trong MySQL (cho đến khi các CHECK
ràng buộc được thực thi).
Cách mô phỏng một số CHECK
ràng buộc trong MySQL
Vì vậy, nếu bạn thích các ràng buộc toàn vẹn tham chiếu và muốn tránh các kích hoạt (vì các vấn đề trong MySQL khi bạn có cả hai trong các bảng của mình), bạn có thể sử dụng một bảng tham chiếu nhỏ khác:
CREATE TABLE age_allowed
( age TINYINT UNSIGNED NOT NULL
, PRIMARY KEY (age)
) ENGINE = InnoDB ;
Điền vào nó với 20 hàng:
INSERT INTO age_allowed
(age)
VALUES
(0), (1), (2), (3), ..., (19) ;
Sau đó, bảng của bạn sẽ là:
CREATE TABLE test
( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT
, age TINYINT UNSIGNED NOT NULL
, PRIMARY KEY (id)
, CONSTRAINT age_allowed__in__test
FOREIGN KEY (age)
REFERENCES age_allowed (age)
) ENGINE = InnoDB ;
Bạn sẽ phải xóa quyền truy cập ghi vào age_allowed
bảng, để tránh việc vô tình thêm hoặc xóa hàng.
Thủ thuật này sẽ không hoạt động với FLOAT
các cột kiểu dữ liệu, thật không may (quá nhiều giá trị giữa 0.0
và 20.0
).
Cách mô phỏng các CHECK
ràng buộc tùy ý trong MySQL (5.7) và MariaDB (từ 5.2 đến 10.1)
Do MariaDB đã thêm các cột được tính toán trong phiên bản 5.2 của họ ( phiên bản GA: 2010-11-10 ) và MySQL trong 5.7 (phiên bản GA: 2015-10-21 ) - mà họ gọi chúng VIRTUAL
và GENERATED
tương ứng - có thể được duy trì, tức là được lưu trữ trong bảng - họ gọi chúng PERSISTENT
và STORED
tương ứng - chúng ta có thể sử dụng chúng để đơn giản hóa giải pháp trên và thậm chí tốt hơn, mở rộng nó để mô phỏng / thực thi các CHECK
ràng buộc tùy ý ):
Như trên, chúng ta sẽ cần một bảng trợ giúp nhưng với một hàng duy nhất lần này sẽ đóng vai trò là bảng "neo". Thậm chí tốt hơn, bảng này có thể được sử dụng cho bất kỳ số lượng CHECK
ràng buộc.
Sau đó, chúng tôi thêm một cột được tính toán để đánh giá TRUE
/ FALSE
/ UNKNOWN
, chính xác như một CHECK
ràng buộc sẽ - nhưng cột này có một FOREIGN KEY
ràng buộc đối với bảng neo của chúng tôi. Nếu điều kiện / cột ước tính FALSE
cho một số hàng, các hàng bị từ chối, do FK.
Nếu điều kiện / cột ước tính thành TRUE
hoặc UNKNOWN
( NULL
), các hàng không bị từ chối, chính xác như điều đó sẽ xảy ra với các CHECK
ràng buộc:
CREATE TABLE truth
( t BIT NOT NULL,
PRIMARY KEY (t)
) ENGINE = InnoDB ;
-- Put a single row:
INSERT INTO truth (t)
VALUES (TRUE) ;
-- Then your table would be:
-- (notice the change to `FLOAT`, to prove that we don't need)
-- (to restrict the solution to a small type)
CREATE TABLE test
( id SMALLINT UNSIGNED NOT NULL AUTO_INCREMENT,
age FLOAT NOT NULL,
age_is_allowed BIT -- GENERATED ALWAYS
AS (age >= 0 AND age < 20) -- our CHECK constraint
STORED,
PRIMARY KEY (id),
CONSTRAINT check_age_must_be_non_negative_and_less_than_20
FOREIGN KEY (age_is_allowed)
REFERENCES truth (t)
) ENGINE = InnoDB ;
Ví dụ dành cho phiên bản MySQL 5.7. Trong MariaDB (phiên bản 5.2 trở lên đến 10.1), chúng ta chỉ cần sửa đổi cú pháp và khai báo cột PERSISTENT
thay vì STORED
. Trong phiên bản 10.2, STORED
từ khóa cũng được thêm vào, vì vậy ví dụ trên hoạt động ở cả hai hương vị (MySQL và MariaDB) cho các phiên bản mới nhất.
Nếu chúng ta muốn thực thi nhiều CHECK
ràng buộc (phổ biến trong nhiều thiết kế), chúng ta chỉ cần thêm một cột được tính toán và một khóa ngoại cho mỗi một trong số chúng. Chúng ta chỉ cần một truth
bảng trong cơ sở dữ liệu. Nó nên có một hàng được chèn và sau đó tất cả quyền truy cập ghi bị xóa.
Tuy nhiên, trong MariaDB mới nhất, chúng tôi không phải thực hiện tất cả các màn nhào lộn này nữa, vì các CHECK
ràng buộc đã được triển khai trong phiên bản 10.2.1 (phiên bản alpha: 2016-Jul-04)!
Phiên bản 10.2.2 hiện tại vẫn là phiên bản beta nhưng có vẻ như tính năng này sẽ có trong phiên bản ổn định đầu tiên của dòng MariaDB 10.2.
Như tôi đã giải thích trong bài viết này , bắt đầu với phiên bản 8.0.16, MySQL đã thêm hỗ trợ cho các ràng buộc CHECK tùy chỉnh:
ALTER TABLE topic
ADD CONSTRAINT post_content_check
CHECK (
CASE
WHEN DTYPE = 'Post'
THEN
CASE
WHEN content IS NOT NULL
THEN 1
ELSE 0
END
ELSE 1
END = 1
);
ALTER TABLE topic
ADD CONSTRAINT announcement_validUntil_check
CHECK (
CASE
WHEN DTYPE = 'Announcement'
THEN
CASE
WHEN validUntil IS NOT NULL
THEN 1
ELSE 0
END
ELSE 1
END = 1
);
Trước đây, điều này chỉ khả dụng khi sử dụng các trình kích hoạt TRƯỚC và TRƯỚC KHI CẬP NHẬT:
CREATE
TRIGGER post_content_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
IF NEW.DTYPE = 'Post'
THEN
IF NEW.content IS NULL
THEN
signal sqlstate '45000'
set message_text = 'Post content cannot be NULL';
END IF;
END IF;
END;
CREATE
TRIGGER post_content_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
IF NEW.DTYPE = 'Post'
THEN
IF NEW.content IS NULL
THEN
signal sqlstate '45000'
set message_text = 'Post content cannot be NULL';
END IF;
END IF;
END;
CREATE
TRIGGER announcement_validUntil_check BEFORE INSERT
ON topic
FOR EACH ROW
BEGIN
IF NEW.DTYPE = 'Announcement'
THEN
IF NEW.validUntil IS NULL
THEN
signal sqlstate '45000'
set message_text = 'Announcement validUntil cannot be NULL';
END IF;
END IF;
END;
CREATE
TRIGGER announcement_validUntil_update_check BEFORE UPDATE
ON topic
FOR EACH ROW
BEGIN
IF NEW.DTYPE = 'Announcement'
THEN
IF NEW.validUntil IS NULL
THEN
signal sqlstate '45000'
set message_text = 'Announcement validUntil cannot be NULL';
END IF;
END IF;
END;
Để biết thêm chi tiết về mô phỏng các ràng buộc CHECK bằng cách sử dụng kích hoạt cơ sở dữ liệu cho các phiên bản MySQL trước 8.0.16, sau đó xem bài viết này .