MySQL CHECK 约束模拟
简介:在本教程中,您将学习如何使用触发器或视图来模拟MySQL中的CHECK约束。
SQL CHECK约束简介
SQL标准为您提供了 CHECK约束,允许您在插入和更新之前验证列或列组的数据。例如,您可以定义CHECK
约束以强制零件的成本为正,如下所示:
CREATE TABLE IF NOT EXISTS parts ( part_no VARCHAR(18) PRIMARY KEY, description VARCHAR(40), cost DECIMAL(10 , 2 ) NOT NULL CHECK(cost > 0), price DECIMAL (10,2) NOT NULL );SQL标准还允许您将多个
CHECK
约束应用于多个列的列或code
CHECK约束。例如,要确保价格始终大于或等于成本,请使用以下CHECK
约束:
CREATE TABLE IF NOT EXISTS parts ( part_no VARCHAR(18) PRIMARY KEY, description VARCHAR(40), cost DECIMAL(10 , 2 ) NOT NULL CHECK (cost > 0), price DECIMAL(10 , 2 ) NOT NULL CHECK (price > 0), CHECK (price >= cost) );一旦
CHECK
约束到位,无论何时插入或更新导致布尔表达式求值为false的值,数据库系统都会拒绝更改。
不幸的是,MySQL不支持CHECK
约束。事实上,MySQL的CREATE TABLE语句接受CHECK
像SQL标准的声明CHECK
语句。但是,MySQL默默地忽略CHECK
约束并且不执行数据验证。
要CHECK
在MySQL中实现约束,可以使用触发器或视图。
使用触发器的MySQL CHECK约束
要CHECK
在MySQL中模拟约束,可以使用两个触发器:BEFORE INSERT
和BEFORE UPDATE
。
首先,创建一个新表parts
演示:
CREATE TABLE IF NOT EXISTS parts ( part_no VARCHAR(18) PRIMARY KEY, description VARCHAR(40), cost DECIMAL(10 , 2 ) NOT NULL, price DECIMAL(10,2) NOT NULL );接下来,创建一个存储过程以检查
cost
和price
列中的值。
DELIMITER $ CREATE PROCEDURE `check_parts`(IN cost DECIMAL(10,2), IN price DECIMAL(10,2)) BEGIN IF cost < 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'check constraint on parts.cost failed'; END IF; IF price < 0 THEN SIGNAL SQLSTATE '45001' SET MESSAGE_TEXT = 'check constraint on parts.price failed'; END IF; IF price < cost THEN SIGNAL SQLSTATE '45002' SET MESSAGE_TEXT = 'check constraint on parts.price & parts.cost failed'; END IF; END$ DELIMITER ;然后,创建
BEFORE INSERT
并BEFORE UPDATE
触发。在触发器内部,调用check_parts()
存储过程。
-- before insert DELIMITER $ CREATE TRIGGER `parts_before_insert` BEFORE INSERT ON `parts` FOR EACH ROW BEGIN CALL check_parts(new.cost,new.price); END$ DELIMITER ; -- before update DELIMITER $ CREATE TRIGGER `parts_before_update` BEFORE UPDATE ON `parts` FOR EACH ROW BEGIN CALL check_parts(new.cost,new.price); END$ DELIMITER ;之后,插入满足以下所有条件的新行:
- cost> 0
- price> 0
- price> =cost
INSERT INTO parts(part_no, description,cost,price) VALUES('A-001','Cooler',100,120);
Query OK, 1 row affected (0.01 sec)
INSERT
语句调用BEFORE INSERT
触发器并接受值。
以下INSERT
语句失败,因为它违反了条件:cost> 0。
INSERT INTO parts(part_no, description,cost,price) VALUES('A-002','Heater',-100,120);
ERROR 1644 (45000): check constraint on parts.cost failed以下
INSERT
语句失败,因为它违反了条件:price> 0。
INSERT INTO parts(part_no, description,cost,price) VALUES('A-002','Heater',100,-120);
ERROR 1644 (45001): check constraint on parts.price failed以下
INSERT
语句失败,因为它违反了以下条件:price> cost。
INSERT INTO parts(part_no, description,cost,price) VALUES('A-003','wiper',120,100);
ERROR 1644 (45002): check constraint on parts.price & parts.cost failed让我们看看我们现在在
parts
表上有什么内容。
SELECT * FROM parts;
+---------+-------------+--------+--------+ | part_no | description | cost | price | +---------+-------------+--------+--------+ | A-001 | Cooler | 100.00 | 120.00 | +---------+-------------+--------+--------+ 1 row in set (0.00 sec)以下语句尝试更新成本以使其低于价格:
UPDATE parts SET price = 10 WHERE part_no = 'A-001';
ERROR 1644 (45002): check constraint on parts.price & parts.cost failed更新被拒绝。 因此,通过使用两个触发器:
BEFORE INSERT
并且BEFORE UPDATE
,您可以在MySQL中模拟CHECK
约束。
使用Views检查MySQL CHECK约束
我们的想法是创建一个带有基表检查选项的视图。在SELECT
视图的语句中,我们只选择满足CHECK
条件的有效行。如果对视图进行任何插入或更新,将导致新行不显示在视图中。
首先,删除parts
表以删除所有关联的触发器并创建一个新的表,如parts
表,但具有不同的名称parts_data
:
DROP TABLE IF EXISTS parts; CREATE TABLE IF NOT EXISTS parts_data ( part_no VARCHAR(18) PRIMARY KEY, description VARCHAR(40), cost DECIMAL(10 , 2 ) NOT NULL, price DECIMAL(10,2) NOT NULL );接下来,创建
parts
基于parts_data
表命名的视图。通过这样做,我们可以保持使用parts
表的应用程序的代码保持不变。此外,旧parts
表的所有权限保持不变。
CREATE VIEW parts AS SELECT part_no, description, cost, price FROM parts_data WHERE cost > 0 AND price > 0 AND price >= cost WITH CHECK OPTION;然后,通过
parts
视图在parts_data
表中插入一个新行:
INSERT INTO parts(part_no, description,cost,price) VALUES('A-001','Cooler',100,120);它被接受,因为新行有效,它出现在视图中。 之后,尝试插入一个不会出现在视图中的新行。
INSERT INTO parts_checked(part_no, description,cost,price) VALUES('A-002','Heater',-100,120);
ERROR 1146 (42S02): Table 'mysqldemo.parts_checked' doesn't exist在本教程中,您已经了解了
CHECK
SQL标准中的约束以及如何使用触发器或视图来模拟MySQL中的CHECK
约束。