MySQL权限篇之REFERENCES以及外键级联操作

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> 

和文档描述一致。

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值