MySQL多表查詢教程
多表查詢 : 連線查詢-子查詢
MySQL基礎操作連結 ; 工具: SQLyog
1. 表結構
使用者,角色,許可權三張表(主表)及三者之間的關係通過兩張 “第三張外來鍵表”維護。“外來鍵表”中的兩個欄位分別使用外來鍵指向主表的主鍵。(一個使用者可以有多個角色,一個角色可以有多個許可權;正常來看是是一對多的關係,但是反過來 某個許可權可以有多個角色擁有。 所以三者關係必須理解為多對多的關係,所以需要 “第三張外來鍵表”去維護兩張表之間的關係,同時保證實體表與實體表之間互相獨立)
表名:
使用者資訊表user |
||
欄位 |
型別 |
描述 |
id |
int(11) NOT NULL |
主鍵,自增 |
name |
varchar(255) NOT NULL |
名稱 |
password |
varchar(255) NOT NULL |
密碼 |
age |
int(11) NOT NULL |
年齡 |
gender |
char(1) NOT NULL |
性別 |
角色資訊表role |
||
欄位 |
型別 |
描述 |
id |
int(11) NOT NULL |
主鍵,自增 |
rolename |
varchar(255) NOT NULL |
名稱 |
許可權資訊表permission |
||
欄位 |
型別 |
描述 |
id |
int(11) NOT NULL |
主鍵,自增 |
name |
varchar(255) NOT NULL |
許可權 |
使用者角色表user_role |
||
欄位 |
型別 |
描述 |
uid |
int(11) NOT NULL |
外來鍵:user id |
rid |
int(11) NOT NULL |
外來鍵:role id |
角色許可權表role_permisson |
||
欄位 |
型別 |
描述 |
rid |
int(11) NOT NULL |
外來鍵:role id |
pid |
int(11) NOT NULL |
外來鍵:permission id |
具體建表語句:
建立使用者表:
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵,自增',
`name` varchar(255) NOT NULL COMMENT '名稱',
`password` varchar(255) NOT NULL COMMENT '密碼',
`age` int(11) NOT NULL COMMENT '年齡',
`gender` char(1) NOT NULL COMMENT '性別',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
建立角色表:
CREATE TABLE `role` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵,自增',
`rolename` varchar(255) NOT NULL COMMENT '名稱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8
建立許可權表:
CREATE TABLE `permission` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
建立使用者角色表:
CREATE TABLE `user_role` (
`uid` int(11) DEFAULT NULL COMMENT '外來鍵-userId',
`rid` int(11) DEFAULT NULL COMMENT '外來鍵-roleId',
KEY `fk_ur_role_id` (`rid`),
KEY `fk_ur_user_id` (`uid`),
CONSTRAINT `fk_ur_user_id` FOREIGN KEY (`uid`) REFERENCES `user` (`id`),
CONSTRAINT `fk_ur_role_id` FOREIGN KEY (`rid`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
建立角色許可權表:
CREATE TABLE `role_permission` (
`rid` int(11) DEFAULT NULL COMMENT '外來鍵-roleId',
`pid` int(11) DEFAULT NULL COMMENT '外來鍵-permissionId',
KEY `fk_rp_role_id` (`rid`),
KEY `fk_rp_permission_id` (`pid`),
CONSTRAINT `fk_rp_role_id` FOREIGN KEY (`rid`) REFERENCES `role` (`id`),
CONSTRAINT `fk_rp_permission_id` FOREIGN KEY (`pid`) REFERENCES `permission` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
2. 查詢操作
(三表聯查)查詢某個許可權有哪些角色?(如:查詢許可權有哪些角色)
子查詢
步驟:
- 1. 先從permission表中查詢出擁有‘查詢’許可權的id
SELECT p.id, p.name FROM permission p WHERE p.name='查詢';
- 2. 從role_permission表(中間表)中查詢出擁有‘查詢’(pid)許可權的角色rid
SELECT rp.rid ,rp.pid FROM role_permission rp WHERE rp.pid = (SELECT p.id FROM permission p WHERE p.name='查詢');
- 3. 最後從role表中獲取資訊:條件 role.id = role_permission.rid
注:通過子查詢在role_permission表中查詢出的rid有多個值(1,2,3),則需要在括號前面新增any欄位。Any關鍵字 表示 where r.id = rp.rid1 or r.id = rp.rid2 or ... 。(擴充套件:all關鍵字用and替換or;some關鍵字和any關鍵字相同)。
否則報錯:Subquery returns more than 1 row
SELECT r.id,r.rolename FROM role r WHERE r.id=ANY(
SELECT rp.rid FROM role_permission rp WHERE rp.pid = (SELECT p.id FROM permission p WHERE p.name='查詢')
);
子查詢--內連線join ... on ...
- 內連線:兩張表合併,條件 rp.pid=p.id AND p.name='查詢' 。
SELECT * FROM role_permission rp JOIN permission p ON rp.pid=p.id AND p.name='查詢' ORDER BY rp.rid ASC;
- 子查詢:
SELECT * FROM role r WHERE r.id=ANY(
SELECT rp.rid FROM role_permission rp JOIN permission p ON rp.pid=p.id AND p.name='查詢'
);
內連線Join...on...
先“中間表”與permission表內連線 -查詢出許可權為‘查詢’的欄位,再與role表進行內連線 -查詢出role表中的角色有哪些。(若資料量大時,不使用select *並新增limit限制行數)
SELECT * FROM role_permission rp JOIN permission p ON rp.pid=p.id AND p.name='查詢' JOIN role r ON rp.rid=r.id;
(五表聯查)查詢某使用者 有哪些角色並且有哪些許可權?(如:A使用者有哪些角色和擁有哪些許可權)
內連線
SELECT * FROM USER u
JOIN user_role ur ON u.id=ur.uid AND u.name='管理員A'
JOIN role r ON ur.rid=r.id
JOIN role_permission rp ON r.id=rp.rid
JOIN permission p ON p.id=rp.pid ;
查詢後的資料:角色及許可權有重複,如圖,
若想得到使用者擁有的角色:
SELECT * FROM USER u JOIN user_role ur ON u.id=ur.uid AND u.name='管理員A' LEFT JOIN role r ON ur.rid=r.id ;
若想得到使用者擁有的許可權:在語句後面使用 分組: group by p.name
SELECT * FROM USER u
JOIN user_role ur ON u.id=ur.uid AND u.name='管理員A'
JOIN role r ON ur.rid=r.id
JOIN role_permission rp ON r.id=rp.rid
JOIN permission p ON p.id=rp.pid
GROUP BY p.name;
注: 去重可以使用distinct 和group by 。 顯然關聯查詢中不能使用distinct。原因:distinct位置固定只能跟在select 後面如(select distinct name from...),且只適用於查詢某個欄位;若需要查詢多個欄位(如 select distinct name ,age from ...),則mysql會過濾(name欄位並且age欄位)也相同的資料。
後續...
附:檢視mysql查詢效率--explain的用法
explain語句用於檢視一條SQL語句的查詢執行計劃,直接把explain放到要執行的SQL語句的前面即可。
例:explain select * from ...
explain extended和explain的輸出結果一樣,只是用explain extended語句後可以通過show warnings檢視一條SQL語句的反編譯的結果,讓我們知道我們輸入的一條SQL語句真正是怎麼執行的。
對輸入結果簡單解釋一下:
- select_type:表示select型別,常見的取值有SIMPLE(不使用表連線或子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION中的或者後面的查詢語句)、SUBQUERY(子查詢中的第一個select)等。
- table:輸出結果集的表。
- type:表示表的連線型別,效能由好到差的連線型別為
- system(表中僅有一行,即常量表)、
- const(單表中最多有一個匹配行,例如PRIMARY KEY或者UNIQUE INDEX)、
- eq_ref(對於前面的每一行,在此表中只查詢一條記錄,簡單來說,就是多表連線中使用PRIMARYKEY或者UNIQUE INDEX)、
- ref(與eq_ref類似,區別在於不使用PRIMARYKEY或者UNIQUE INDEX,而是使用普通的索引)、
- ref_of_null(與ref類似,區別在於條件中包含對NULL的查詢)
- index_merge(索引合併化)、
- unique_subquery(in的後面是一個查詢主鍵欄位的子查詢)、
- index_subquery(與unique_subquery類似,區別在於in的後面是查詢非唯一索引欄位的子查詢)、
- range(單表中的範圍查詢)、
- index(對於前面的每一行都通過查詢索引來得到資料)、
- all(對於前面的每一行的都通過全表掃描來獲得資料)。
結果值從好到壞依次是:system > const > eq_ref > ref > fulltext > ref_or_null >index_merge > unique_subquery > index_subquery > range > index > ALL
一般來說,得保證查詢至少達到range級別,最好能達到ref。
- possible_keys:表示查詢時,可能使用到的索引。
- key:表示實際使用的索引
- key_len:索引欄位的長度
- rows:掃描行的數量
- extra:執行情況的說明和描述。