獨立個人博客網(wǎng)站制作微信公眾號怎么開通
文章目錄
- 預備工作
- 一、表的基本查詢
- 1、簡單基本查詢
- 2、分組聚合統(tǒng)計
- 3、基本查詢練習
- 二、表的復合查詢
- 1、多表查詢
- 2、子查詢
- 2.1 **單行子查詢**
- 2.2 **多行子查詢**
- 2.3 **多列子查詢**
- 2.4 在from子句中使用子查詢
- 3、合并查詢
- 三、表的連接
- 1、自連接
- 2、內(nèi)連接
- 3、外連接
預備工作
scott 數(shù)據(jù)庫是 oracle 9i 的經(jīng)典測試數(shù)據(jù)庫,用于為初學者提供一些簡單的應用示例,便于初學者進行練習,其中的表和表間的關系演示了關系型數(shù)據(jù)庫的一些基本原理。本文所有的查詢工作都是基于 scott 數(shù)據(jù)庫進行的,scott 數(shù)據(jù)庫的 .sql 文件代碼如下:
DROP database IF EXISTS `scott`;
CREATE database IF NOT EXISTS `scott` DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;USE `scott`;DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept` (`deptno` int(2) unsigned zerofill NOT NULL COMMENT '部門編號',`dname` varchar(14) DEFAULT NULL COMMENT '部門名稱',`loc` varchar(13) DEFAULT NULL COMMENT '部門所在地點'
);DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp` (`empno` int(6) unsigned zerofill NOT NULL COMMENT '雇員編號',`ename` varchar(10) DEFAULT NULL COMMENT '雇員姓名',`job` varchar(9) DEFAULT NULL COMMENT '雇員職位',`mgr` int(4) unsigned zerofill DEFAULT NULL COMMENT '雇員領導編號',`hiredate` datetime DEFAULT NULL COMMENT '雇傭時間',`sal` decimal(7,2) DEFAULT NULL COMMENT '工資月薪',`comm` decimal(7,2) DEFAULT NULL COMMENT '獎金',`deptno` int(2) unsigned zerofill DEFAULT NULL COMMENT '部門編號'
);DROP TABLE IF EXISTS `salgrade`;
CREATE TABLE `salgrade` (`grade` int(11) DEFAULT NULL COMMENT '等級',`losal` int(11) DEFAULT NULL COMMENT '此等級最低工資',`hisal` int(11) DEFAULT NULL COMMENT '此等級最高工資'
);insert into dept (deptno, dname, loc)
values (10, 'ACCOUNTING', 'NEW YORK');
insert into dept (deptno, dname, loc)
values (20, 'RESEARCH', 'DALLAS');
insert into dept (deptno, dname, loc)
values (30, 'SALES', 'CHICAGO');
insert into dept (deptno, dname, loc)
values (40, 'OPERATIONS', 'BOSTON');insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7844, 'TURNER', 'SALESMAN', 7698,'1981-09-08', 1500, 0, 30);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20);insert into emp (empno, ename, job, mgr, hiredate, sal, comm, deptno)
values (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);insert into salgrade (grade, losal, hisal) values (1, 700, 1200);
insert into salgrade (grade, losal, hisal) values (2, 1201, 1400);
insert into salgrade (grade, losal, hisal) values (3, 1401, 2000);
insert into salgrade (grade, losal, hisal) values (4, 2001, 3000);
insert into salgrade (grade, losal, hisal) values (5, 3001, 9999);
大家可以在自己的工作目錄下創(chuàng)建 scott_data.sql 文件,然后將上述代碼拷貝進去,最后在 mysql 中使用 source 語句將其導入即可。
scott 數(shù)據(jù)庫中一共有三張表 – emp、dept、salgrade,它們分別代表員工信息、部門信息以及薪資等級信息,具體的表結構以及表中數(shù)據(jù)如下:
一、表的基本查詢
1、簡單基本查詢
MySQL 表查詢的基本語法格式如下:
SELECT
[DISTINCT] {* | {column [, column] ...}
[FROM table_name]
[WHERE ...]
[ORDER BY column [ASC | DESC], ...]
LIMIT ...
其中 select from where 是查詢的基本關鍵字,其余部分關鍵字的含義如下:
- distinct:對查詢到的結果進行去重。
- order by:按照某一列或某幾列對查詢結果進行排序,默認使用 ASC 排升序,排降序可以指定 DESC。
- limit:篩選分頁結果,即指定顯示查詢結果的哪些行。(注:對未知表進行查詢時,最好加一條 LIMIT 1,避免因為表中數(shù)據(jù)過大,查詢?nèi)頂?shù)據(jù)導致數(shù)據(jù)庫卡死)
注意:MySQL 不區(qū)分大小寫和單雙引號,所以這些關鍵字在使用是無論是大寫還是小寫都可以。
下面是關于針對這些關鍵字使用的一些基本案例:
- 查找SMITH員工的基本信息,包括員工名、部門號以及薪資。
select ename, deptno, sal from emp where ename = 'SMITH';
- 查詢所有員工的工資信息,并按工資降序排序。
select ename, sal from emp order by sal desc;
- 查找公司中工齡排名前三的員工。
select ename, hiredate from emp order by hiredate asc limit 3;
2、分組聚合統(tǒng)計
聚合統(tǒng)計
MySQL 中存在一些用于對數(shù)據(jù)進行計算和匯總的聚合函數(shù),它們可以對一組行的數(shù)據(jù)執(zhí)行操作,并返回單個結果。常見的聚合函數(shù)如下:
-函數(shù) | -說明 |
---|---|
COUNT([DISTINCT] expr) | 返回查詢到的數(shù)據(jù)的 數(shù)量 |
SUM([DISTINCT] expr) | 返回查詢到的數(shù)據(jù)的 總和,不是數(shù)字沒有意義 |
AVG([DISTINCT] expr) | 返回查詢到的數(shù)據(jù)的 平均值,不是數(shù)字沒有意義 |
MAX([DISTINCT] expr) | 返回查詢到的數(shù)據(jù)的 最大值,不是數(shù)字沒有意義 |
MIN([DISTINCT] expr) | 返回查詢到的數(shù)據(jù)的 最小值,不是數(shù)字沒有意義 |
- 統(tǒng)計公司一共有多少員工以及公司所有的最高、最低工資分別是多少。
select count(*) 員工數(shù)量, max(sal) 最高工資, min(sal) 最低工資 from emp;
分組聚合統(tǒng)計
除了聚合統(tǒng)計,MySQL 還支持在 select 中使用 group by 子句對指定列進行分組查詢,group by 字句通常需要配合聚合函數(shù)使用。
- 如何顯示每個部門的平均工資和最高工資。
select deptno, max(sal) 最高工資, min(sal) 最低工資 from emp group by deptno;
如何理解分組 (重要):
在上面的案例中,我們要求每個部門的平均工資與最高工作,然而 scott 中并沒有為每一個部門單獨 create 一個工資表,而只有一個 emp 表,里面包含了所有部門的員工信息,所以我們需要對 emp 表按照 deptno 進行分組。
按照 deptno 進行 group by 之后,一張物理上的 emp 表就在邏輯上被分為了三張子表,每張子表中員工的部門號是相同的;所以我們就可以 將分組理解為分表 – 這個分表不是真的將存儲在數(shù)據(jù)庫中的一張 emp 表分為了三張表,而是將 emp 分成了邏輯上的三張表。
那么現(xiàn)在,我們只需要分別對每一張子表進行聚合統(tǒng)計得到最高工資和平均工作即可;所以,通過 “分表”,我們就可以將分組查詢簡化理解為對分組得到的子表的查詢,只需要在最后面添加 group by 子句即可。
注:在分組查詢中,select 后面的列信息通常只能包含聚合函數(shù)以及出現(xiàn)在分組條件中的列。
現(xiàn)在我們用 “分表” 的思想來求一下每個部門的每種崗位的平均工資和最低工資:
求每個部門每種崗位的平均工資與最低工資,肯定需要按部門和崗位進行分組,而分組就是分表,所以我們可以理解為對分組后得到的子表進行聚合統(tǒng)計查詢平均工資和最低工資 –
select deptno, job, avg(sal), min(sal) from emp
,最后再加上group by deptno, job
即可。
select deptno, job, avg(sal) 平均工資, max(sal) 最高工資 from emp group by deptno, job;
having 條件篩選
having 用于和 group by 配合使用,對 group by 的結果進行過濾。
- 顯示平均工資低于2000的部門和它的平均工資。
select deptno, avg(sal) 平均工資 from emp group by deptno having 平均工資 < 2000;
如何理解 where 和 having 的關系:
where 和 having 都是用于條件篩選的關鍵字,二者的區(qū)別在于 where 主要用于在查詢一張表時對查詢條件做篩選;而 having 主要用于在分組時對分組的結果進行篩選。其實把 having 當成 where 來用 MySQL 的語法也不會報錯,但我們不建議這樣做。
SQL 查詢中各個關鍵字的執(zhí)行先后順序:
from > on > join > where > group by > with > having > select > distinct > order by > limit
3、基本查詢練習
- 查詢工資高于500或崗位為MANAGER的雇員,同時還要滿足他們的姓名首字母為大寫的J;
select * from emp where (sal > 100 or job = 'MANAGER') and ename like 'J%';
- 按照部門號升序而雇員的工資降序排序;
select * from emp order by deptno asc, sal desc;
- 使用年薪進行降序排序;
select ename, sal*12+ifnull(comm, 0) 年薪 from emp order by 年薪 desc;
- 顯示工資高于平均工資的員工信息;
select * from emp where sal > (select avg(sal) from emp);
- 顯示每個部門的平均工資和最高工資;
select avg(sal) 平均工資, max(sal)最高工資 from emp group by deptno;
- 顯示每種崗位的雇員總數(shù),平均工資;
select job, count(*) 雇員總數(shù), avg(sal) 平均工資 from emp group by job;
二、表的復合查詢
1、多表查詢
上面我們講解的 mysql 表的查詢都是對一張表進行查詢,但在實際開發(fā)中數(shù)據(jù)往往來自不同的表,所以我們需要進行多表查詢。
笛卡爾積
笛卡爾積(Cartesian Product)是指在沒有使用任何條件連接的情況下,將兩個或多個表中的每一行與其他表中的每一行進行組合,從而得到一個包含所有可能組合的表。如下:
- 顯示雇員名、雇員工資以及所在部門的名字。
題目要求我們顯示雇員名、雇員工資以及所在部門的名字,其中名、雇員工資都在 emp 表中,但是部門名字在 dept 表中,所以我們需要對 emp 和 dept 這兩張表做笛卡爾積,然后再篩選掉不需要的行即可。
select ename, sal, dname from emp, dept where emp.deptno = dept.deptno;
- 顯示各個員工的姓名,工資,及工資級別;
select ename, sal, grade from emp, salgrade where sal between losal and hisal;
總結:其實多表查詢本質上也是單表查詢 – 對兩張表笛卡爾積的結果 (單表) 進行條件篩選與查詢。
2、子查詢
子查詢是指嵌入在其他 sql 語句中的 select 語句,也叫嵌套查詢。
2.1 單行子查詢
單行子查詢是指嵌入在其他 sql 語句中的 select 語句的結果只有一行一列,即單個數(shù)據(jù)。
- 顯示SMITH同一部門的員工。
顯示SMITH同一部門的員工一共分為兩步:1. 找出SMITH所在的部門;2. 找出在此部門工作的員工。而第一步的結果是單個數(shù)據(jù)(一個員工只屬于一個部門),這就是單行子查詢。
select * from emp where deptno = (select deptno from emp where ename = 'SMITH');
2.2 多行子查詢
單行子查詢是指嵌入在其他 sql 語句中的 select 語句的結果有多行,但只有一列,即多個數(shù)據(jù)。
與多行子查詢相關的關鍵字有三個:
- in:表示在其中,即與多個數(shù)據(jù)中的一個相等即可。
- all:表示全部,即大于/小于/… 多個數(shù)據(jù)中的全部。
- any:表示任意,即大于/小于/… 多個數(shù)據(jù)中的任意一個。
- 查詢和10號部門的工作崗位相同的雇員的名字,崗位,工資,部門號,但是不包含10自己的。
select ename, job, sal, deptno from emp where job in (select job from emp where deptno = 10) and deptno != 10;
- 顯示工資比部門30的所有員工的工資高的員工的姓名、工資和部門號。
select ename, sal, deptno from emp where sal > all (select sal from emp where deptno = 30);
- 顯示工資比部門30的任意員工的工資高的員工的姓名、工資和部門號(包含自己部門的員工)。
select ename, sal, deptno from emp where sal > any (select sal from emp where deptno = 30);
2.3 多列子查詢
多列子查詢是指嵌入在其他 sql 語句中的 select 語句的結果有多列 (不一定有多行)。多列子查詢中也可以使用 in/all/any 關鍵字。
- 查詢和SMITH的部門和崗位完全相同的所有雇員,不含SMITH本人。
select ename from emp where (deptno, job) = (select deptno, job from emp where ename='SMITH') and ename <> 'SMITH';
2.4 在from子句中使用子查詢
對 mysql 表的理解
在前面分組聚合統(tǒng)計中我們提到,分組其實就是 “分表”,我們可以將分組的結果當成邏輯上的子表來看待,然后分組查詢就簡化為了對子表進行查詢,而這其實就是最基礎的表查詢。
同樣,我們也可以將 select 查詢過程中的中間結果以及查詢出來的最終結果都看作是邏輯上的表,那么我們自然也就可以將這個 “表” 放在 from 子句的后面了。
所以,我們可以認為 mysql 中一切皆表,任何表的查詢其本質上都是單表查詢,這和我們 Linux 中的一切皆文件很類似。
比如,我們要顯示每個高于自己部門平均工資的員工的姓名、部門、工資、平均工資,我們可以一步步的來拆解這個查詢:
-
查出每個部門的平均工資:
select deptno, avg(sal) avg_sal from emp group by deptno;
-
將查詢出來的 “deptno, avg_sal” 這個中間結果當成表,與 emp 表進行笛卡爾積,得到一張新的表:
select * from emp t1, (select deptno, avg(sal) avg_sal from emp group by deptno) t2;
-
在這個大的表中找出是同一部門且工作高于部門平均工資 agv_sal 的員工的姓名、部門、工資以及部門的平均工資:
select t1.ename, t1.deptno, t1.sal, t2.avg_sal from emp t1, (select deptno, avg(sal) avg_sal from emp group by deptno) t2 where t1.deptno = t2.deptno and t1.sal > t2.avg_sal;
查找每個部門工資最高的人的姓名、工資、部門、最高工資:
-
找出每個部門工資最高的人:
select deptno, max(sal) max_sal from emp group by deptno;
-
將這個表與 emp 表進行笛卡爾積:
select * from emp t1, (select deptno, max(sal) max_sal from emp group by deptno) t2;
-
在這個大表中找出同一部門且工資等于部門最高工資的員工的姓名、工資、部門以及部門的最高工資:
select t1.ename, t1.sal, t1.deptno, t2.max_sal from emp t1, (select deptno, max(sal) max_sal from emp group by deptno) t2 where t1.deptno = t2.deptno and t1.sal = t2.max_sal;
- 顯示每個部門的信息(部門名,編號,地址)和人員數(shù)量。
select t1.dname, t1.deptno, t1.loc, t2.peo_num from dept t1, (select deptno, count(*) peo_num from emp group by deptno) t2 where t1.deptno = t2.deptno;
3、合并查詢
在實際應用中,為了合并多個 select 的執(zhí)行結果,可以使用集合操作符 union,union all。
union
union 操作符用于取得兩個結果集的并集,當使用該操作符時,會自動去掉結果集中的重復行。
- 將工資大于2500或職位是MANAGER的人找出來。
select * from emp where sal > 2500 union select * from emp where job = 'MANAGER';
union all
union all 操作符用于取得兩個結果集的并集,當使用該操作符時,不會去掉結果集中的重復行。
select * from emp where sal > 2500 union all select * from emp where job = 'MANAGER';
三、表的連接
1、自連接
自連接是指在同一張表上進行連接查詢,即自己與自己做笛卡爾積。
- 顯示員工FORD的上級領導的編號和姓名。
select t2.empno, t2.ename from emp t1, emp t2 where t1.ename = 'FORD' and t2.empno = t1.mgr;
2、內(nèi)連接
內(nèi)連接實際上就是利用 where 子句對兩種表形成的笛卡兒積進行篩選,我們前面學習的查詢本質上也是內(nèi)連接,內(nèi)連接也是在開發(fā)過程中使用的最多的連接查詢。
內(nèi)連接語法如下:
select 字段 from 表1 inner join 表2 on 連接條件 and 其他條件;
- 顯示SMITH的名字和部門名稱。
select emp.ename, dept.dname from emp inner join dept on emp.deptno = dept.deptno and emp.ename = 'SMITH';
3、外連接
外連接分為左外連接和右外連接。
左外連接
左外連接是指左邊表中的數(shù)據(jù)保持不變,右邊表中的數(shù)據(jù)按照篩選條件過濾,記錄不足的列使用 NULL 填充,然后將二者連接起來。
語法如下:
select 字段名 from 表名1 left join 表名2 on 連接條件
- 列出部門名稱和這些部門的員工信息,同時列出沒有員工的部門。
select dept.dname, emp.* from dept left join emp on dept.deptno = emp.deptno;
右外連接
右外連接是指右外表中的數(shù)據(jù)保持不變,右外表中的數(shù)據(jù)按照篩選條件過濾,記錄不足的列使用 NULL 填充,然后將二者連接起來。
語法如下:
select 字段名 from 表名1 right join 表名2 on 連接條件
注:其實左外連接完全可以實現(xiàn)右外連接的效果 – 將左右兩張表的順序交換即可。
- 列出部門名稱和這些部門的員工信息,同時列出沒有員工的部門。
select dept.dname, emp.* from emp right join dept on dept.deptno = emp.deptno;