This slide is based on Jeffrey D. Ullman's work, which can be download from his website.
References: 刘增杰、张少军 《MySQL 5.5 从零开始学》
在属性类型声明后面加上
CREATE TABLE Beers ( name CHAR(20) UNIQUE, manf CHAR(20) );
CREATE TABLE Sells ( bar CHAR(20), beer VARCHAR(20), price REAL, PRIMARY KEY (bar, beer) );
某个关系中某个属性或属性组上的值, 也必在另一个关系的某个或某些属性上出现.
例如: 在
使用关键词
被引用的另一个关系中的属性必须(在它所在的关系中)被声明为
CREATE TABLE Beers ( name CHAR(20) PRIMARY KEY, manf CHAR(20) ); CREATE TABLE Sells ( bar CHAR(20), beer CHAR(20) REFERENCES Beers(name), price REAL, PRIMARY KEY(bar,beer) );
mysql> alter table Sells -> add foreign key(beer) references Beers(name) -> on delete cascade -> on update cascade;
要删除刚才建立的外键约束, 则使用下面的语句
mysql> alter table Sells drop foreign key Sells_ibfk_1;
这里的
mysql> show create table Sells\G; *************************** 1. row *************************** Table: Sells Create Table: CREATE TABLE `Sells` ( `bar` varchar(20) NOT NULL DEFAULT '', `beer` varchar(20) NOT NULL DEFAULT '', `price` double DEFAULT NULL, PRIMARY KEY (`bar`,`beer`), KEY `beer` (`beer`), CONSTRAINT `Sells_ibfk_1` FOREIGN KEY (`beer`) REFERENCES `Beers` (`name`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
要注意的是, 如果用
mysql> alter table Sells -> add foreign key (beer) references Beers (name) -> on delete set null -> on update cascade; ERROR 1005 (HY000): Can't create table 'test_bar.#sql-35e_2' (errno: 150) mysql> desc Beers;
CREATE TABLE Beers ( name CHAR(20) PRIMARY KEY, manf CHAR(20) ); CREATE TABLE Sells ( bar CHAR(20), beer CHAR(20), price REAL, FOREIGN KEY (beer) REFERENCES Beers(name) );
如果关系
对于第一种, 系统拒绝这种违法修改. 但是, 对于在被引用关系上的修改, 设计者可以有三种选择:
例子: 设
对于
从
将
从
将
当我们声明一个外键时, 对于删除和更新操作可以独立地设置置空(
在外键的声明之后加上
如果不作出上面的选取, 则采用缺省原则(即拒绝更新).
CREATE TABLE Sells ( bar CHAR(20), beer CHAR(20), price REAL, FOREIGN KEY(beer) REFERENCES Beers(name) ON DELETE SET NULL ON UPDATE CASCADE );
试一下下面的语句是否能够创建外键
CREATE TABLE Sells ( bar CHAR(20), beer CHAR(20) REFERENCES Beers(name) ON DELETE SET NULL ON UPDATE CASCADE, price REAL );
事实上, 上面的语句能运行, 但是并没有创建外键.
对特定属性的值作约束, 可以在对该属性声明数据类型时加上
条件
CREATE TABLE Sells ( bar CHAR(20), beer CHAR(20) CHECK ( beer IN (SELECT name FROM Beers)), price REAL CHECK ( price <= 5.00 ) );
在 MySQL5.5 版本中, 上面的语句可以成功创建表
在 MySQL8.4.2 版本中, 可以起作用.
将下面的代码输入 mysql 交互式终端. (假设使用的是 MySQL 8.4.2)
CREATE TABLE voter( voter_id INTEGER PRIMARY KEY, name TEXT, age INTEGER, date_of_birth DATE, check(age >= 18) );
然后输入
Table: voter Create Table: CREATE TABLE `voter` ( `voter_id` int NOT NULL, `name` text, `age` int DEFAULT NULL, `date_of_birth` date DEFAULT NULL, PRIMARY KEY (`voter_id`), CONSTRAINT `voter_chk_1` CHECK ((`age` >= 18)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
当我们尝试插入年龄小于 18 的数据时便会发生错误.
INSERT INTO voter(voter_id, name, age, date_of_birth) VALUE (123, "Mike", 16, '2008-02-01'); ERROR: 3819 (HY000): Check constraint 'voter_chk_1' is violated.
基于属性的检查仅在属性的值被插入或更新时才进行.
这里的
检查仅在元组被插入或更新时进行.
只有
CREATE TABLE Sells ( bar CHAR(20), beer CHAR(20), price REAL, CHECK (bar = ’Joe’’s Bar’ OR price <= 5.00) );
在 MySQL5.5 版本中, 上面的语句可以成功创建表
登录 MySQL(5.7.23), 发现
mysql> select version(); +-----------+ | version() | +-----------+ | 5.7.23 | +-----------+ 1 row in set (0.04 sec) mysql> ALTER TABLE sells -> ADD CHECK (bar='Joe'' Bar' OR price<=5.00); Query OK, 0 rows affected (0.06 sec) Enregistrements: 0 Doublons: 0 Avertissements: 0
成功. 但是查看表的信息
CREATE TABLE `sells` ( `bar` varchar(20) NOT NULL, `beer` varchar(20) NOT NULL, `price` double DEFAULT NULL, PRIMARY KEY (`bar`,`beer`) ) ENGINE=MyISAM DEFAULT CHARSET=latin1
表并没有发生改变. 依然可以插入不符合约束的数据.
mysql> INSERT INTO sells(bar, beer, price) -> VALUE ('Jack''s Bar', 'Chimay', 6.00); Query OK, 1 row affected (0.04 sec)
登录 MySQL 8.4.2, 发现
CREATE TABLE `sells` ( `bar` varchar(20) NOT NULL, `beer` varchar(20) NOT NULL, `price` double DEFAULT NULL, PRIMARY KEY (`bar`,`beer`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
现在增加
mysql> ALTER TABLE Sells -> ADD CHECK (Bar='Joe''s Bar' OR price <=5.00); ERROR 3819 (HY000): Check constraint 'sells_chk_1' is violated.
出现这种错误的原因是原表中存在违反约束的数据.
mysql> select * from sells; +------------------+---------------+-------+ | bar | beer | price | +------------------+---------------+-------+ | 3DArtBar | 喜力 | 25 | | 3DArtBar | 嘉士伯 | 32 | | 3DArtBar | 安贝夫 | 40 | | 3DArtBar | 百威 | 30 | | 3DArtBar | 贝克 | 35 | | HardRock | 健力士黑啤 | 39 | | HardRock | 喜力 | 28 | | HardRock | 嘉士伯 | 30 | | HardRock | 安贝夫 | 39 | | HardRock | 百威 | 32 | | HardRock | 科罗娜 | 40 | | HardRock | 贝克 | 33 | | Westside | 健力士黑啤 | 39 | | Westside | 南非啤酒 | 36 | | Westside | 朝日 | 26 | | Westside | 百威 | 32 | | Westside | 科罗娜 | 40 | | Westside | 纯种苦啤酒 | 33 | | Westside | 贝克 | 28 | | 宝莱纳餐厅 | 健力士黑啤 | 39 | | 宝莱纳餐厅 | 南非啤酒 | 36 | | 宝莱纳餐厅 | 喜力 | 28 | | 宝莱纳餐厅 | 朝日 | 26 | | 宝莱纳餐厅 | 百威 | 32 | | 宝莱纳餐厅 | 科罗娜 | 40 | | 宝莱纳餐厅 | 纯种苦啤酒 | 33 | | 扬州老啤酒厂酒吧 | Stella Artois | 36 | | 扬州老啤酒厂酒吧 | 健力士黑啤 | 26 | | 扬州老啤酒厂酒吧 | 喜力 | 35 | | 扬州老啤酒厂酒吧 | 安贝夫 | 40 | | 扬州老啤酒厂酒吧 | 百威 | 32 | | 扬州老啤酒厂酒吧 | 科罗娜 | 40 | | 扬州老啤酒厂酒吧 | 米勒 | 50 | | 扬州老啤酒厂酒吧 | 纯种苦啤酒 | 33 | | 扬州老啤酒厂酒吧 | 贝克 | 42 | | 扬州老啤酒厂酒吧 | 麒麟 | 39 | | 木板房啤酒吧 | 嘉士伯 | 43 | | 木板房啤酒吧 | 安贝夫 | 40 | | 木板房啤酒吧 | 生力 | 25 | | 木板房啤酒吧 | 米勒 | 32 | | 木板房啤酒吧 | 贝克 | 35 | | 木板房啤酒吧 | 麒麟 | 30 | | 苏荷酒吧 | 健力士黑啤 | 39 | | 苏荷酒吧 | 南非啤酒 | 36 | | 苏荷酒吧 | 喜力 | 28 | | 苏荷酒吧 | 朝日 | 26 | | 苏荷酒吧 | 生力 | 38 | | 苏荷酒吧 | 百威 | 32 | | 苏荷酒吧 | 科罗娜 | 40 | | 苏荷酒吧 | 纯种苦啤酒 | 33 | +------------------+---------------+-------+ 50 rows in set (0.01 sec)
mysql> ALTER TABLE sells -> ADD CHECK (price<=50); Query OK, 50 rows affected (0.07 sec) Records: 50 Duplicates: 0 Warnings: 0
此时就成功添加了约束, 因为表中原有数据最大值是50.
再检查
CREATE TABLE `sells` ( `bar` varchar(20) NOT NULL, `beer` varchar(20) NOT NULL, `price` double DEFAULT NULL, PRIMARY KEY (`bar`,`beer`), CONSTRAINT `sells_chk_1` CHECK ((`price` <= 50)) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
使用
mysql> ALTER TABLE Sells -> DROP sells_chk_1; ERROR 1091 (42000): Can't DROP 'sells_chk_1'; check that column/key exists
mysql> ALTER TABLE Sells -> DROP CONSTRAINT sells_chk_1; Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
mysql> select * from drinkers; Empty set (0.02 sec)
mysql> desc drinkers; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | name | varchar(20) | NO | PRI | NULL | | | addr | varchar(255) | YES | | NULL | | | phone | varchar(15) | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 3 rows in set (0.00 sec)
将
mysql> ALTER TABLE drinkers -> DROP COLUMN phone; Query OK, 0 rows affected (0.03 sec) Records: 0 Duplicates: 0 Warnings: 0
检查后发现
mysql> DESC drinkers; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | name | varchar(20) | NO | PRI | NULL | | | addr | varchar(255) | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 2 rows in set (0.04 sec)
为继续实验, 再将
mysql> ALTER TABLE drinkers -> ADD COLUMN phone VARCHAR(15); Query OK, 0 rows affected (0.05 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> DESC drinkers; +-------+--------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+--------------+------+-----+---------+-------+ | name | varchar(20) | NO | PRI | NULL | | | addr | varchar(255) | YES | | NULL | | | phone | varchar(15) | YES | | NULL | | +-------+--------------+------+-----+---------+-------+ 3 rows in set (0.00 sec)
断言是数据库模式(schema)的一部分, 等同于表(relation)或视图(view).
断言的定义是:
MySQL 5.5 不支持断言. 但是可以使用触发器来实现断言所述的功能.
由于
mysql> select version(); +-----------+ | version() | +-----------+ | 8.4.2 | +-----------+ 1 row in set (0.01 sec)
mysql> CREATE TABLE Sells2 LIKE Sells; Query OK, 0 rows affected (0.04 sec)
mysql> show tables; +--------------------+ | Tables_in_test_bar | +--------------------+ | bars | | beers | | drinkers | | frequents | | likes | | potbuddies | | sells | | sells2 | +--------------------+ 8 rows in set (0.00 sec)
mysql> DESC Sells; +-------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +-------+-------------+------+-----+---------+-------+ | bar | varchar(20) | NO | PRI | NULL | | | beer | varchar(20) | NO | PRI | NULL | | | price | double | YES | | NULL | | +-------+-------------+------+-----+---------+-------+ 3 rows in set (0.00 sec)
注: 此时表
若要复制完整的表(即结构和数据都要复制), 则使用
CREATE TABLE Sells2 AS SELECT * FROM Sells;
mysql> DROP TABLE Sells2; Query OK, 0 rows affected (0.04 sec) mysql> CREATE TABLE Sells2 AS SELECT * FROM Sells; Query OK, 50 rows affected (0.03 sec) Records: 50 Duplicates: 0 Warnings: 0
我们清空
mysql> TRUNCATE TABLE Sells2; Query OK, 0 rows affected (0.04 sec) mysql> SELECT * FROM Sells2; Empty set (0.00 sec)
在
CREATE ASSERTION NoRipoffBars CHECK( NOT EXISTS ( SELECT bar FROM Sells GROUP BY bar HAVING 5.00 < AVG(price) ));
其中子查询所得的结果关系是那些啤酒平均售价超过 $\$ 5$ 的酒吧.
mysql> CREATE ASSERTION NoRipoffBars CHECK( -> NOT EXISTS ( -> SELECT bar FROM Sells -> GROUP BY bar -> HAVING 5.00 < AVG(price) -> )); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ASSERTION NoRipoffBars CHECK( NOT EXISTS ( SELECT bar FROM Sells ' at line 1 mysql>
mysql> CREATE ASSERTION NoRipoffBars CHECK(price<60); ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'ASSERTION NoRipoffBars CHECK(price<60)' at line 1
对于关系
CREATE ASSERTION FewBar CHECK ( (SELECT COUNT(*) FROM Bars) <= (SELECT COUNT(*) FROM Drinkers) );
原则上, 对于数据库中任何关系的更改, 每个断言都必须执行.
一个聪明的系统可以观察到仅某些更改会导致断言被违反.
断言的确很强大, 但
基于属性和基于元组的检查, 知道何时检查, 但它们不够强大.
触发器允许用户在给定时机检查任何给定的条件.
MySQL 的触发器和存储过程一样, 都是嵌入到 MySQL 的一段程序.
触发器是由事件来触发某个操作, 这些事件包括
外键约束将使得一个在
CREATE TRIGGER BeerTrig BEFORE INSERT ON Sells REFERENCING NEW ROW AS NewTuple FOR EACH ROW WHEN (NewTuple.beer NOT IN (SELECT name FROM Beers)) INSERT INTO Beers(name) VALUES(NewTuple.beer);
MySQL 中上面的代码会出错. MySQL 中是用
CREATE TRIGGER trigger_name trigger_time trigger_event ON tbl_name FOR EACH ROW trigger_stmt
触发器程序可以使用
mysql> delimiter // mysql> CREATE TRIGGER ins_check BEFORE INSERT ON Sells -> FOR EACH ROW -> BEGIN -> IF NEW.price < 0 THEN -> SET NEW.price = 0; -> ELSEIF NEW.price > 100 THEN -> SET NEW.price = 100; -> END IF; -> END// mysql> delimiter ;
继续实验, 看看会有什么问题
mysql> delimiter // mysql> CREATE TRIGGER PriceTrig -> AFTER UPDATE ON Sells -> FOR EACH ROW -> BEGIN -> IF NEW.price > OLD.price +1.00 THEN -> SET NEW.price = OLD.price +1.00; -> END IF; -> END// mysql> delimiter ; ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger
mysql> delimiter // mysql> CREATE TRIGGER PriceTrig -> BEFORE UPDATE ON Sells -> FOR EACH ROW -> BEGIN -> IF NEW.price > OLD.price +1.00 THEN -> SET NEW.price = OLD.price +1.00; -> END IF; -> END// mysql> delimiter ; ERROR 1235 (42000): This version of MySQL doesn't yet support 'multiple triggers with the same action time and event for one table'
请先删除刚才建立的触发器, 然后完成触发器的创建.
删除触发器的语句是:
mysql> drop trigger ins_check;
例子: 单执行语句的触发器
首先建立一张表
mysql> CREATE TABLE account( -> acct_num INT, -> amount DECIMAL(10,2) -> ); Query OK, 0 rows affected (0.04 sec)
然后针对这张表的插入事件建立名为
mysql> CREATE TRIGGER ins_sum BEFORE INSERT -> ON account -> FOR EACH ROW -> SET @sum=@sum+NEW.amount; Query OK, 0 rows affected (0.03 sec)
最后, 先设置用户变量, 然后执行插入.
mysql> SET @sum=0; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO account -> VALUES (1,1.00), (2,2.00); Query OK, 2 rows affected (0.01 sec) Records: 2 Duplicates: 0 Warnings: 0
检查用户变量
mysql> SELECT @sum; +------+ | @sum | +------+ | 3.00 | +------+ 1 row in set (0.00 sec)
或者
CREATE OR REPLACE TRIGGER <name>
触发器或者是 "row-level" 或者是 "statement-level".
Refer to these by
可以是任意返回布尔值的条件.
条件(condition)在触发器事件之前或之后(取决于使用的是
动作中可以有不止一个
但是查询在动作中没有太大的实际意义(尽管是可以的, 可能有的时候也是需要的, 比如自动化显示等等), 因此我们这里只限定为修改.
将关系
CREATE TRIGGER PriceTrig AFTER UPDATE OF price ON Sells REFERENCING OLD ROW AS ooo NEW ROW AS nnn FOR EACH ROW WHEN (nnn.price > ooo.price + 1.00) INSERT INTO RipoffBars VALUES (nnn.bar);
mysql> delimiter // mysql> CREATE TRIGGER PriceTrig -> AFTER UPDATE ON Sells -> FOR EACH ROW -> BEGIN -> IF NEW.price > OLD.price +1.00 THEN -> INSERT INTO RipoffBars VALUES (NEW.bar); -> END IF; -> END// mysql> delimiter ;
如果指明对属性 price 进行修改, 试一下下面的语句
mysql> delimiter // mysql> CREATE TRIGGER PriceTrig -> AFTER UPDATE of Price ON Sells -> FOR EACH ROW -> BEGIN -> IF NEW.price > OLD.price +1.00 THEN -> INSERT INTO RipoffBars VALUES (NEW.bar); -> END IF; -> END// mysql> delimiter ;
会导致语法错误.
另一个要注意的是, 尽管 RipoffBars 这个表没有建立, 但是触发器也是可以建立的.
查看建立的触发器
mysql> SHOW TRIGGERS\G;
创建表
create table t(s1 integer);
创建触发器
delimiter | CREATE TRIGGER t_trigger BEFORE INSERT ON t FOR EACH ROW BEGIN SET @x = "hello trigger"; SET NEW.s1 = 55; END; | delimiter ;
最后一行 delimiter ; 是恢复分号作为语句结束符, 注意有 delimiter 和 ; 之间有空格.
查看触发器
show triggers\G;
删除触发器
drop trigger t_trigger;
参考自 http://oak.cs.ucla.edu/cs143/project/mysql/assertion.html
DELIMITER // -- change the end of statement delimiter from ; to // CREATE TRIGGER CS143Mandatory AFTER INSERT ON Student FOR EACH ROW -- note that "REFERECING NEW ROW AS NEW" is missing BEGIN IF NEW.GPA > 2.0 THEN -- this is equivalent to WHEN (NEW.GPA > 2.0) INSERT INTO Enroll VALUES (NEW.sid, 'CS', 143, 1); END IF; END // -- mark the end of trigger with // DELIMITER ; -- change the delimiter back to ;
我们可以推出表
创建一个单执行语句的触发器. 由于是单条语句, 故不必更换分隔符.
CREATE TABLE account( acct_num INT, amount DECIMAL(10,2) ); CREATE TRIGGER ins_sum BEFORE INSERT ON account FOR EACH ROW SET @sum=@sum+NEW.amount;
mysql> CREATE TABLE account( -> acct_num INT, -> amount DECIMAL(10,2) -> ); Query OK, 0 rows affected (0.04 sec) mysql> CREATE TRIGGER ins_sum -> BEFORE INSERT ON account -> FOR EACH ROW -> SET @sum=@sum+NEW.amount; Query OK, 0 rows affected (0.03 sec) mysql> SET @sum=0; Query OK, 0 rows affected (0.00 sec) mysql> INSERT INTO account VALUES(1,1.00),(2,2.00),(3,3.00),(4,4.00),(5,5.00); Query OK, 5 rows affected (0.01 sec) Records: 5 Duplicates: 0 Warnings: 0 mysql> SELECT @sum; +-------+ | @sum | +-------+ | 15.00 | +-------+ 1 row in set (0.00 sec)
注意, 当 MySQL 重新启动后, 变量
mysql> select @sum; +------+ | @sum | +------+ | NULL | +------+ 1 row in set (0.00 sec)
此时将执行语句放在
CREATE TABLE test1( a1 INT; ); CREATE TABLE test2( a2 INT; ); CREATE TABLE test3( a3 INT; ); CREATE TABLE test4( a4 INT; b4 INT; );
DELIMITER // CREATE TRIGGER testref BEFORE INSERT ON test1 FOR EACH ROW BEGIN INSERT INTO test2 VALUES(NEW.a1); DELETE FROM test3 WHERE a3=NEW.a1; UPDATE test4 SET b4=b4+1 WHERE a4=NEW.a1; END // DELIMITER ;
Before MySQL 5.7.2, there cannot be multiple triggers for a given table that have the same trigger event and action time. For example, you cannot have two BEFORE UPDATE triggers for a table.
关于
我们以书上第7章 P.197 图7-5的例子, 将之改写为 MySQL 下可以运行的触发器.
CREATE TRIGGER NetWorthTrigger AFTER UPDATE ON MovieExec FOR EACH ROW BEGIN IF OLD.netWorth > NEW.netWorth THEN UPDATE MovieExec SET netWorth = OLD.netWorth WHERE certNo = NEW.certNo; END IF; END;//
CREATE TRIGGER NetWorthTrigger AFTER UPDATE OF netWorth ON MovieExec REFERENCING OLD ROW AS OldTuple, NEW ROW AS NewTuple FOR EACH ROW WHEN (OldTuple.netWorth > NewTuple.netWorth) UPDATE MovieExec SET netWorth = OldTuple.netWorth WHERE cert# = NewTuple.cert#;
回顾
mysql> show create table user\G; *************************** 1. row *************************** Table: user Create Table: CREATE TABLE `user` ( `id` char(9) NOT NULL, `username` varchar(20) DEFAULT NULL, `password` char(41) DEFAULT NULL, `timestamp` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=gbk 1 row in set (0.00 sec) ERROR: No query specified
这里报错的原因是在使用
mysql> show create table studentinfo\G *************************** 1. row *************************** Table: studentinfo Create Table: CREATE TABLE `studentinfo` ( `studentID` char(9) NOT NULL, `name` varchar(20) DEFAULT NULL, `gender` char(4) DEFAULT NULL, `department` varchar(20) DEFAULT NULL, `major` varchar(30) DEFAULT NULL, `class` varchar(10) DEFAULT NULL, `QQ` varchar(20) DEFAULT NULL, `email` varchar(50) DEFAULT NULL, `phone` varchar(11) DEFAULT NULL, `state` varchar(10) DEFAULT NULL, `course_attribute` varchar(10) DEFAULT NULL, `remark` varchar(100) DEFAULT NULL, PRIMARY KEY (`studentID`) ) ENGINE=MyISAM DEFAULT CHARSET=gbk 1 row in set (0.00 sec)
在
mysql> ALTER TABLE user -> ADD CONSTRAINT fk_user_id FOREIGN KEY (id) REFERENCES studentInfo(studentID); Query OK, 50 rows affected (0.09 sec) Enregistrements: 50 Doublons: 0 Avertissements: 0
mysql> SHOW CREATE TABLE user\G *************************** 1. row *************************** Table: user Create Table: CREATE TABLE `user` ( `id` char(9) NOT NULL, `username` varchar(20) DEFAULT NULL, `password` char(41) DEFAULT NULL, `timestamp` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=gbk 1 row in set (0.04 sec)
发现没有外键. 原因是
mysql> ALTER TABLE user -> ENGINE=InnoDB; Query OK, 50 rows affected (0.11 sec) Enregistrements: 50 Doublons: 0 Avertissements: 0 mysql> SHOW CREATE TABLE user\G *************************** 1. row *************************** Table: user Create Table: CREATE TABLE `user` ( `id` char(9) NOT NULL, `username` varchar(20) DEFAULT NULL, `password` char(41) DEFAULT NULL, `timestamp` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=gbk 1 row in set (0.01 sec)
mysql> ALTER TABLE user -> ADD CONSTRAINT fk_user_id FOREIGN KEY (id) REFERENCES studentInfo(studentID); ERROR 1215 (HY000): Impossible d'ajouter des contraintes d'index externe
无法添加外部索引约束.
mysql> ALTER TABLE studentInfo -> ENGINE=InnoDB; Query OK, 50 rows affected (0.16 sec) Enregistrements: 50 Doublons: 0 Avertissements: 0 mysql> SHOW CREATE TABLE studentInfo\G *************************** 1. row *************************** Table: studentInfo Create Table: CREATE TABLE `studentinfo` ( `studentID` char(9) NOT NULL, `name` varchar(20) DEFAULT NULL, `gender` char(4) DEFAULT NULL, `department` varchar(20) DEFAULT NULL, `major` varchar(30) DEFAULT NULL, `class` varchar(10) DEFAULT NULL, `QQ` varchar(20) DEFAULT NULL, `email` varchar(50) DEFAULT NULL, `phone` varchar(11) DEFAULT NULL, `state` varchar(10) DEFAULT NULL, `course_attribute` varchar(10) DEFAULT NULL, `remark` varchar(100) DEFAULT NULL, PRIMARY KEY (`studentID`) ) ENGINE=InnoDB DEFAULT CHARSET=gbk 1 row in set (0.02 sec)
再次尝试对
mysql> ALTER TABLE user -> ADD CONSTRAINT fk_user_id FOREIGN KEY (id) REFERENCES studentInfo(studentID); Query OK, 50 rows affected (0.17 sec) Enregistrements: 50 Doublons: 0 Avertissements: 0
添加成功.
mysql> SHOW CREATE TABLE user\G *************************** 1. row *************************** Table: user Create Table: CREATE TABLE `user` ( `id` char(9) NOT NULL, `username` varchar(20) DEFAULT NULL, `password` char(41) DEFAULT NULL, `timestamp` timestamp NULL DEFAULT NULL, PRIMARY KEY (`id`), CONSTRAINT `fk_user_id` FOREIGN KEY (`id`) REFERENCES `studentinfo` (`studentID`) ) ENGINE=InnoDB DEFAULT CHARSET=gbk 1 row in set (0.05 sec)
两个属性的