1. 程式人生 > >MySQL多表查詢教程

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:表示表的連線型別,效能由好到差的連線型別為
    1. system(表中僅有一行,即常量表)、
    2. const(單表中最多有一個匹配行,例如PRIMARY KEY或者UNIQUE INDEX)、
    3. eq_ref(對於前面的每一行,在此表中只查詢一條記錄,簡單來說,就是多表連線中使用PRIMARYKEY或者UNIQUE INDEX)、
    4. ref(與eq_ref類似,區別在於不使用PRIMARYKEY或者UNIQUE INDEX,而是使用普通的索引)、
    5. ref_of_null(與ref類似,區別在於條件中包含對NULL的查詢)
    6. index_merge(索引合併化)、
    7. unique_subquery(in的後面是一個查詢主鍵欄位的子查詢)、
    8. index_subquery(與unique_subquery類似,區別在於in的後面是查詢非唯一索引欄位的子查詢)、
    9. range(單表中的範圍查詢)、
    10. index(對於前面的每一行都通過查詢索引來得到資料)、
    11. 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:執行情況的說明和描述。
  •