REFERENCES,对象权限。
建立外键关系权限。
用户要在tb1上建立外键,外键指向tb2,那么该用户必须在tb2上有REFERENCES权限。
当然,还要有在tb1上alter的权限。
比如:
mysql> show grants for 'ut01'@'%';
+-----------------------------------------------------+
| Grants for ut01@% |
+-----------------------------------------------------+
| GRANT USAGE ON *.* TO 'ut01'@'%' |
| GRANT REFERENCES ON `test`.`sys_menu` TO 'ut01'@'%' |
| GRANT ALTER ON `test`.`sys_role_menu` TO 'ut01'@'%' |
+-----------------------------------------------------+
3 rows in set (0.00 sec)
mysql>
在sys_menu上有references权限,在sys_role_menu上有alter权限。
来看该用户的操作:
C:\Users\Administrator>mysql -u'ut01'
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 5.7.11-log MySQL Community Server (GPL)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use test
Database changed
mysql> ALTER TABLE `test`.`sys_role_menu`
-> ADD FOREIGN KEY (`rmenMenuId`) REFERENCES `test`.`sys_menu`(`menuId`) ON UPDATE CASCADE ON DELETE CASCADE;
ERROR 1142 (42000): ALTER command denied to user 'ut01'@'localhost' for table 'sys_role_menu' #当该用户在sys_role_menu上没有alter权限时
mysql> ALTER TABLE `test`.`sys_role_menu`
-> ADD FOREIGN KEY (`rmenMenuId`) REFERENCES `test`.`sys_menu`(`menuId`) ON UPDATE CASCADE ON DELETE CASCADE;
Query OK, 4 rows affected (1.19 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql>
红色部分ON UPDATE CASCADE ON DELETE CASCADE;表示当父表列值变更时,子表的动作为级联更改。
ON UPDATE CASCADE 表示当父表更新时,子表也级联更新。
ON DELETE CASCADE 表示当父表删除时,子表也级联删除。
在定义外键关系时,父表的更新(或者删除)动作,可级联子表操作有如下四种定义方式:
CASCADE,表示子表列值随父表一起变化(更新也一起更新,删除也一起删除)。注意,子表上级联更改的列,不会激活触发器。
NO ACTION,表示父表列值被保护(阻止更改,不能有任何动作)。
RESTRICT,表示父表列值被保护(限制更改,不能有任何动作,效果和NO ACTION一致,其实就是NO ACTION的别名)。也是外键级联操作的默认行为。
SET NULL,表示父表列值被更改时,关联的子表列值置空(前提是子表该列属性可为NULL)。
对于非innodb表,还有一个方式是:SET DEFAULT,但是innodb表,该定义不合法。
下面是演示:
D:\temp>mysql
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 5.7.11-log MySQL Community Server (GPL)
Copyright (c) 2000, 2016, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> use test
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| sys_menu |
| sys_role |
| sys_role_menu |
+----------------+
3 rows in set (0.00 sec)
mysql> SELECT
-> *
-> FROM
-> `KEY_COLUMN_USAGE`
-> WHERE table_schema = 'test'
-> AND REFERENCED_COLUMN_NAME IS NOT NULL;
ERROR 1146 (42S02): Table 'test.key_column_usage' doesn't exist
mysql> SELECT
-> *
-> FROM
-> information_schema.`KEY_COLUMN_USAGE`
-> WHERE table_schema = 'test'
-> AND REFERENCED_COLUMN_NAME IS NOT NULL;
Empty set (0.01 sec)
#表示test库没有目前还没有外键关系存在
mysql> select * from sys_menu;
+--------+--------------+-----------+
| menuId | menuParentId | menuUrlId |
+--------+--------------+-----------+
| 201 | 0 | 9001 |
| 202 | 201 | 9002 |
| 203 | 201 | 9003 |
| 204 | 202 | 9004 |
| 205 | 203 | 9005 |
| 206 | 204 | 9006 |
| 207 | 201 | 9007 |
| 208 | 202 | 9008 |
+--------+--------------+-----------+
8 rows in set (0.00 sec)
mysql> select * from sys_role_menu;
+--------+------------+------------+
| rmenId | rmenRoleId | rmenMenuId |
+--------+------------+------------+
| 301 | 101 | 201 |
| 302 | 101 | 202 |
| 303 | 101 | 205 |
| 304 | 102 | 206 |
+--------+------------+------------+
4 rows in set (0.00 sec)
mysql>
下面建立外键关系:
mysql> ALTER TABLE `test`.`sys_role_menu`
-> ADD FOREIGN KEY (`rmenMenuId`) REFERENCES `test`.`sys_menu`(`menuId`) ON UPDATE CASCADE;
Query OK, 4 rows affected (0.83 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql>
#已经添加父表更新,子表级联更新的外键。
当父表更新时:
mysql> select * from sys_menu;
+--------+--------------+-----------+
| menuId | menuParentId | menuUrlId |
+--------+--------------+-----------+
| 201 | 0 | 9001 |
| 202 | 201 | 9002 |
| 203 | 201 | 9003 |
| 204 | 202 | 9004 |
| 205 | 203 | 9005 |
| 206 | 204 | 9006 |
| 207 | 201 | 9007 |
| 208 | 202 | 9008 |
+--------+--------------+-----------+
8 rows in set (0.00 sec)
mysql> select * from sys_role_menu;
+--------+------------+------------+
| rmenId | rmenRoleId | rmenMenuId |
+--------+------------+------------+
| 301 | 101 | 201 |
| 302 | 101 | 202 |
| 303 | 101 | 205 |
| 304 | 102 | 206 |
+--------+------------+------------+
4 rows in set (0.00 sec)
mysql> update sys_menu set menuId=212 where menuId=202
Query OK, 1 row affected (0.12 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from sys_menu;
+--------+--------------+-----------+
| menuId | menuParentId | menuUrlId |
+--------+--------------+-----------+
| 201 | 0 | 9001 |
| 203 | 201 | 9003 |
| 204 | 202 | 9004 |
| 205 | 203 | 9005 |
| 206 | 204 | 9006 |
| 207 | 201 | 9007 |
| 208 | 202 | 9008 |
| 212 | 201 | 9002 |
+--------+--------------+-----------+
8 rows in set (0.00 sec)
mysql> select * from sys_role_menu;
+--------+------------+------------+
| rmenId | rmenRoleId | rmenMenuId |
+--------+------------+------------+
| 301 | 101 | 201 |
| 302 | 101 | 212 |
| 303 | 101 | 205 |
| 304 | 102 | 206 |
+--------+------------+------------+
4 rows in set (0.00 sec)
mysql>
#可见,子表成功级联更新。这是CASCADE。
来看看NO ACTION:
mysql> ALTER TABLE `test`.`sys_role_menu`
-> DROP FOREIGN KEY `sys_role_menu_ibfk_1`;
Query OK, 0 rows affected (0.18 sec)
Records: 0 Duplicates: 0 Warnings: 0 先删除原有的外键
mysql> ALTER TABLE `test`.`sys_role_menu`
-> ADD FOREIGN KEY (`rmenMenuId`) REFERENCES `test`.`sys_menu`(`menuId`) ON UPDATE NO ACTION;
Query OK, 4 rows affected (0.99 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from sys_menu;
+--------+--------------+-----------+
| menuId | menuParentId | menuUrlId |
+--------+--------------+-----------+
| 201 | 0 | 9001 |
| 203 | 201 | 9003 |
| 204 | 202 | 9004 |
| 205 | 203 | 9005 |
| 206 | 204 | 9006 |
| 207 | 201 | 9007 |
| 208 | 202 | 9008 |
| 212 | 201 | 9002 |
+--------+--------------+-----------+
8 rows in set (0.00 sec)
mysql> select * from sys_role_menu;
+--------+------------+------------+
| rmenId | rmenRoleId | rmenMenuId |
+--------+------------+------------+
| 301 | 101 | 201 |
| 302 | 101 | 212 |
| 303 | 101 | 205 |
| 304 | 102 | 206 |
+--------+------------+------------+
4 rows in set (0.00 sec)
mysql> update sys_menu set menuId=202 where menuId=212;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`sys_role_menu`, CONSTRAINT `sys_role_menu_ibfk_1` FOREIGN KEY (`rmenMenuId`) REFERENCES `sys_menu` (`menuId`) ON UPDATE NO ACTION)
mysql>
NO ACTION,在父表列值上不能有任何动作。
接下来轮到RESTRICT:
mysql> ALTER TABLE `test`.`sys_role_menu`
-> DROP FOREIGN KEY `sys_role_menu_ibfk_1`,
-> ADD FOREIGN KEY (`rmenMenuId`) REFERENCES `test`.`sys_menu`(`menuId`) ON UPDATE RESTRICT;
Query OK, 4 rows affected (1.63 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from sys_menu;
+--------+--------------+-----------+
| menuId | menuParentId | menuUrlId |
+--------+--------------+-----------+
| 201 | 0 | 9001 |
| 203 | 201 | 9003 |
| 204 | 202 | 9004 |
| 205 | 203 | 9005 |
| 206 | 204 | 9006 |
| 207 | 201 | 9007 |
| 208 | 202 | 9008 |
| 212 | 201 | 9002 |
+--------+--------------+-----------+
8 rows in set (0.00 sec)
mysql> select * from sys_role_menu;
+--------+------------+------------+
| rmenId | rmenRoleId | rmenMenuId |
+--------+------------+------------+
| 301 | 101 | 201 |
| 302 | 101 | 212 |
| 303 | 101 | 205 |
| 304 | 102 | 206 |
+--------+------------+------------+
4 rows in set (0.00 sec)
mysql> update sys_menu set menuId=202 where menuId=212;
ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`test`.`sys_role_menu`, CONSTRAINT `sys_role_menu_ibfk_2` FOREIGN KEY (`rmenMenuId`) REFERENCES `sys_menu` (`menuId`))
mysql>
被限制操作。父表列值被限制操作,效果和NO ACTION一致。也是默认的级联行为。
继续看看SET NULL:
mysql> ALTER TABLE `test`.`sys_role_menu`
-> DROP FOREIGN KEY `sys_role_menu_ibfk_2`,
-> ADD FOREIGN KEY (`rmenMenuId`) REFERENCES `test`.`sys_menu`(`menuId`) ON UPDATE SET NULL;
ERROR 1215 (HY000): Cannot add foreign key constraint
mysql>
外键约束添加失败,原因是子表rmenMenuId列NOT NULL导致:
mysql> ALTER TABLE `test`.`sys_role_menu`
-> DROP FOREIGN KEY `sys_role_menu_ibfk_2`;
Query OK, 0 rows affected (0.18 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> ALTER TABLE `test`.`sys_role_menu`
-> CHANGE `rmenMenuId` `rmenMenuId` BIGINT(20) NULL,
-> ADD FOREIGN KEY (`rmenMenuId`) REFERENCES `test`.`sys_menu`(`menuId`) ON UPDATE SET NULL;
Query OK, 4 rows affected (0.91 sec)
Records: 4 Duplicates: 0 Warnings: 0
mysql> select * from sys_menu;
+--------+--------------+-----------+
| menuId | menuParentId | menuUrlId |
+--------+--------------+-----------+
| 201 | 0 | 9001 |
| 203 | 201 | 9003 |
| 204 | 202 | 9004 |
| 205 | 203 | 9005 |
| 206 | 204 | 9006 |
| 207 | 201 | 9007 |
| 208 | 202 | 9008 |
| 212 | 201 | 9002 |
+--------+--------------+-----------+
8 rows in set (0.00 sec)
mysql> select * from sys_role_menu;
+--------+------------+------------+
| rmenId | rmenRoleId | rmenMenuId |
+--------+------------+------------+
| 301 | 101 | 201 |
| 302 | 101 | 212 |
| 303 | 101 | 205 |
| 304 | 102 | 206 |
+--------+------------+------------+
4 rows in set (0.00 sec)
mysql> update sys_menu set menuId=202 where menuId=212;
Query OK, 1 row affected (0.09 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from sys_menu;
+--------+--------------+-----------+
| menuId | menuParentId | menuUrlId |
+--------+--------------+-----------+
| 201 | 0 | 9001 |
| 202 | 201 | 9002 |
| 203 | 201 | 9003 |
| 204 | 202 | 9004 |
| 205 | 203 | 9005 |
| 206 | 204 | 9006 |
| 207 | 201 | 9007 |
| 208 | 202 | 9008 |
+--------+--------------+-----------+
8 rows in set (0.00 sec)
mysql> select * from sys_role_menu;
+--------+------------+------------+
| rmenId | rmenRoleId | rmenMenuId |
+--------+------------+------------+
| 301 | 101 | 201 |
| 302 | 101 | NULL |
| 303 | 101 | 205 |
| 304 | 102 | 206 |
+--------+------------+------------+
4 rows in set (0.00 sec)
mysql>
和文档描述一致。