MySQL学习笔记_上(select查询)
上次整理了一些练习发到博客上了,也说要发基础的,整理了一下午才算是把查询那块的勉强整理完,下次再整理其他的,另外还在写设计模式和数据结构的草稿,写的差不多会慢慢发的,这两项算是副线发展,主线还是按JavaSE–JavaWeb–JavaEE的方向来。因为Java后面都需要数据库,所以就先把数据库先抽空写出来,要成体系嘛,我也会坚持的哈。废话不说了,进入主题吧。
关于查询所需要的表,直接将表贴到这里供大家参考。
employees表:
departments表:
job_grades表:
jobs表:
locations表:
第1章:SQL概述
SQL:Structured Query Language结构化查询语言,它是使用关系模型的数据库应用语言。
1. SQL的语言规范
- mysql对于SQL语句不区分大小写,SQL语句关键字尽量大写。
- SQL 可以写在一行或者多行。为了提高可读性,各子句分行写,必要时使用缩进。
- 关键字不能被缩写也不能分行。
- 值,除了数值型,字符串型和日期时间类型使用单引号(’ ')。
- 别名,尽量使用双引号(" "),而且不建议省略as。
- 所有标点符号使用英文状态下的半角输入方式。
- 必须保证所有(),单引号,双引号是成对结束的。
- 可以使用(1)#单行注释 (2)–空格单行注释 (3)/* 多行注释 */。
2. 命名规则
- 数据库、表名不得超过30个字符,变量名限制为29个。
- 必须只能包含 A–Z, a–z, 0–9, _共63个字符。
- 不能在对象名的字符间留空格。
- 必须不能和用户定义的其他对象重名。
- 必须保证你的字段没有和保留字、数据库系统或常用方法冲突。
- 保持字段名和类型的一致性,在命名字段并为其指定数据类型的时候一定要保证一致性。假如数据类型在一个表里是整数,那在另一个表里可就别变成字符型了。
说明:一个语句可以分开多行编写,以;或\g结束
3. SQL分类
-
DDL(Data Definition Languages):数据定义语言,这些语句定义了不同的数据段、数据库、表、列、索引等数据库对象。
- 主要的语句关键字包括
CREATE
、DROP
、ALTER
等。
- 主要的语句关键字包括
-
DML(Data Manipulation Language):数据操作语言,用于添加、删除、更新和查询数据库记录,并检查数据完整性。
- 主要的语句关键字包括
INSERT
、DELETE
、UPDATE
、SELECT
等。 - SELECT是SQL语言的基础,最为重要。
- 主要的语句关键字包括
-
DCL(Data Control Language):数据控制语言,用于控制不同数据段直接的许可和访问级别的语句。这些语句定义了数据库、表、字段、用户的访问权限和安全级别。
- 主要的语句关键字包括
GRANT
、REVOKE
、COMMIT
、ROLLBACK
、SAVEPOINT
等。
- 主要的语句关键字包括
下面主要说的是DML中的select,关于其他的下篇博客会介绍到。
第2章:数据处理之查询
2.1 基本的SELECT语句
2.1.1 SELECT … FROM
SELECT 标识选择哪些列
FROM 标识从哪个表中选择
- 选择全部列:
SELECT *
FROM departments;
- 选择特定的列:
SELECT department_id, location_id
FROM departments;
2.1.2 列的别名
-
重命名一个列
-
便于计算
-
紧跟列名,也可以在列名和别名之间加入关键字AS,别名使用双引号,以便在别名中包含空格或特殊的字符并区分大小写。
使用别名
SELECT last_name AS name, commission_pct comm
FROM employees;
SELECT last_name "Name", salary*12 "Annual Salary"
FROM employees;
2.1.3 去除重复行
默认情况下,查询会返回全部行,包括重复行。
SELECT department_id
FROM employees;
在SELECT语句中使用关键字DISTINCT去除重复行。
SELECT DISTINCT department_id
FROM employees;
2.1.4 空值参与运算
- 所有运算符或列值遇到null值,运算的结果都为null
SELECT employee_id,salary,commission_pct,
12 * salary * (1 + commission_pct) "annual_sal"
FROM employees;
2.1.5 显示表结构
使用DESCRIBE 或 DESC 命令,表示表结构。
DESCRIBE employees;
2.2 过滤数据
2.2.1 SELECT … FROM … WHERE
- 使用WHERE 子句,将不满足条件的行过滤掉
- WHERE子句紧随 FROM子句
#选择department_id=90的员工信息
SELECT employee_id, last_name, job_id, department_id
FROM employees
WHERE department_id = 90 ;
2.2.2 比较运算符
① 基本操作符
操作符 | 含义 |
---|---|
= | 等于(不是==) |
> | 大于 |
>= | 大于、等于 |
< | 小于 |
<= | 小于、等于 |
<> 或 != | 不等于 |
说明:赋值符号使用 :=
#选择工资小于等于3000的员工姓名和工资
SELECT last_name, salary
FROM employees
WHERE salary <= 3000;
② 其它比较运算符
操作符 | 含义 |
---|---|
BETWEEN … AND | 在两个值之间(包含边界) |
IN(set) | 等于值列表中的一个 |
LIKE | 模糊查询 |
IS NULL | 空值 |
1)BETWEEN … AND
使用 BETWEEN 运算来显示在一个区间内的值
SELECT last_name, salary
FROM employees
WHERE salary BETWEEN 2500 AND 3500;
2)IN
使用 IN运算显示列表中的值。
#选择管理Id为100,101,201的员工信息
SELECT employee_id, last_name, salary, manager_id
FROM employees
WHERE manager_id IN (100, 101, 201);
3)LIKE
-
使用 LIKE 运算选择类似的值
-
选择条件可以包含字符或数字:
- % 代表零个或多个字符(任意个字符)。
- _ 代表一个字符。
#选择first_name第一个字母为S的first_name
SELECT first_name
FROM employees
WHERE first_name LIKE 'S%';
- ‘%’和‘_’可以同时使用。
#第二个字母为o的last_name
SELECT last_name
FROM employees
WHERE last_name LIKE '_o%';
ESCAPE
- 回避特殊符号的:使用转义符。例如:将[%]转为[],然后再加上[ESCAPE‘$’]即可。
SELECT job_id
FROM jobs
WHERE job_id LIKE ‘IT\_%‘;
如果使用\表示转义,要省略ESCAPE。如果不是\,则要加上ESCAPE。
SELECT job_id
FROM jobs
WHERE job_id LIKE ‘IT$_%‘ escape ‘$‘;
4)NULL
使用 IS (NOT) NULL 判断空值。
#选择manager_id为NULL的员工的last_name,manager_id
SELECT last_name, manager_id
FROM employees
WHERE manager_id IS NULL;
2.2.3 逻辑运算符
操作符 | 含义 |
---|---|
&& (或AND) | 逻辑且 |
||(或OR) | 逻辑或 |
NOT | 逻辑否 |
XOR | 逻辑异或 |
1)&& (或AND)
AND要求并的关系为真。
#查询金额大于等于10000并且job_id中有MAN字段的员工信息
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary >=10000
AND job_id LIKE '%MAN%';
2)||(或OR)
OR要求或关系为真。
##查询金额大于等于10000或者job_id中有MAN字段的员工信息
SELECT employee_id, last_name, job_id, salary
FROM employees
WHERE salary >= 10000
OR job_id LIKE '%MAN%';
3)NOT
#查询不在IT_PROG,'ST_CLERK,SA_REP'部门的员工的last_name和job_id
SELECT last_name, job_id
FROM employees
WHERE job_id NOT IN ('IT_PROG', 'ST_CLERK', 'SA_REP');
4)XOR
select last_name,department_id,salary
from employees
where department_id in (10,20) XOR salary > 8000;
2.2.4 算术运算符
运算符 | 说明 |
---|---|
+ | 加法 |
- | 减法 |
* | 乘法 |
/ (或div) | 除法 |
%(或mod) | 取模 |
#查询部门id%2等于0的员工信息
SELECT employee_id,salary,department_id
FROM employees
WHERE department_id MOD 2 = 0;
2.3 排序数据和分页
2.3.1 排序数据
-
使用 ORDER BY 子句排序
- ASC(ascend): 升序
- DESC(descend):降序
-
ORDER BY 子句在SELECT语句的结尾。
#查询按金额升序排序的员工信息
SELECT employee_id, last_name, salary
FROM employees
ORDER BY salary;
#查询按部门id升序排列,相同则按金额降序排列的员工信息。
SELECT last_name, department_id, salary
FROM employees
ORDER BY department_id, salary DESC;
- 可以使用不在SELECT列表中的列排序。
2.3.2 分页
-
MySQL中使用limit实现分页
所谓分页显示,就是将数据库中的结果集,一段一段显示出来需要的条件。
--前10条记录:
SELECT * FROM table LIMIT 0,10;
--第11至20条记录:
SELECT * FROM table LIMIT 10,10;
--第21至30条记录:
SELECT * FROM table LIMIT 20,10;
- 公式**:(当前页数-1)每页条数,每页条数*
SELECT * FROM table
LIMIT(PageNo - 1)*PageSize,PageSize;
-
注意:limit子句必须放在整个查询语句的最后!
2.4 多表查询
2.4.1 MySQL连接
使用连接在多个表中查询数据。
SELECT table1.column, table2.column
FROM table1, table2
WHERE table1.column1 = table2.column2;
-
在 WHERE子句中写入连接条件。
-
在表中有相同列时,在列名之前加上表名前缀。
2.4.2 等值连接
SELECT employees.employee_id, employees.last_name,
employees.department_id, departments.department_id,
departments.location_id
FROM employees, departments
WHERE employees.department_id = departments.department_id;
多个连接条件用 AND 操作符
区分重复的列名
-
使用表名前缀在多个表中区分相同的列。
-
在不同表中具有相同列名的列可以用表的别名加以区分。
表的别名
-
使用别名可以简化查询。
-
使用表名前缀可以提高执行效率。
SELECT e.employee_id, e.last_name, e.department_id,
d.department_id, d.location_id
FROM employees e , departments d
WHERE e.department_id = d.department_id;
连接多个表
连接 n个表,至少需要n-1个连接条件。
例如:连接三个表,至少需要两个连接条件。
查询出公司员工的 last_name,department_name, city
SELECT last_name,department_name,city
FROM employees e , departments d,locations l
WHERE e.department_id = d.department_id
AND d.location_id = l.location_id
2.4.3 自连接 vs 非自连接
- 当两张表是同一张表,只是用取别名的方式虚拟成两张表以代表不同的意义。然后两个表再进行内连接,外连接等查询
题目:查询employees表,返回“Xxx works for Xxx”
SELECT CONCAT(worker.last_name ,' works for '
, manager.last_name)
FROM employees worker, employees manager
WHERE worker.manager_id = manager.employee_id ;
2.4.4 分类3:内连接 vs 外连接
-
内连接: 合并具有同一列的两个以上的表的行, 结果集中不包含一个表与另一个表不匹配的行
-
外连接: 两个表在连接过程中除了返回满足连接条件的行以外还返回左(或右)表中不满足条件的行 ,这种连接称为左(或右) 外连接。没有匹配的行时, 结果表中相应的列为空(NULL)。
① SQL99:使用ON子句创建连接
-
自然连接中是以具有相同名字的列为连接条件的。
-
可以使用 ON 子句指定额外的连接条件。
-
这个连接条件是与其它条件分开的。
-
ON 子句使语句具有更高的易读性。
SELECT e.employee_id, e.last_name, e.department_id,
d.department_id, d.location_id
FROM employees e JOIN departments d
ON (e.department_id = d.department_id);
SELECT employee_id, city, department_name
FROM employees e
JOIN departments d
ON d.department_id = e.department_id
JOIN locations l
ON d.location_id = l.location_id;
② 左外连接
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
LEFT OUTER JOIN departments d
ON (e.department_id = d.department_id) ;
③ 右外连接
SELECT e.last_name, e.department_id, d.department_name
FROM employees e
RIGHT OUTER JOIN departments d
ON (e.department_id = d.department_id) ;
总结:SQL JOINS
补充:
UNION操作符
UNION 操作符返回两个查询的结果集的并集。
UNION ALL操作符
UNION ALL操作符返回两个查询的结果集的并集。对于两个结果集的重复部分,不去重。
2.5 单行函数
2.5.1 介绍
两种SQL函数
单行函数
-
操作数据对象
-
接受参数返回一个结果
-
只对一行进行变换
-
每行返回一个结果
-
可以嵌套
-
参数可以是一列或一个值
2.5.2 字符串函数
函数 | 用法 |
---|---|
CONCAT(S1,S2,…,Sn) | 连接S1,S2,…,Sn为一个字符串 |
CONCAT_WS(s, S1,S2,…,Sn) | 同CONCAT(s1,s2,…)函数,但是每个字符串之间要加上s |
CHAR_LENGTH(s) | 返回字符串s的字符数 |
LENGTH(s) | 返回字符串s的字节数,和字符集有关 |
INSERT(str, index , len, instr) | 将字符串str从第index位置开始,len个字符长的子串替换为字符串instr |
UPPER(s) 或 UCASE(s) | 将字符串s的所有字母转成大写字母 |
LOWER(s) 或LCASE(s) | 将字符串s的所有字母转成小写字母 |
LEFT(s,n) | 返回字符串s最左边的n个字符 |
RIGHT(s,n) | 返回字符串s最右边的n个字符 |
LPAD(str, len, pad) | 用字符串pad对str最左边进行填充,直到str的长度为len个字符 |
RPAD(str ,len, pad) | 用字符串pad对str最右边进行填充,直到str的长度为len个字符 |
LTRIM(s) | 去掉字符串s左侧的空格 |
RTRIM(s) | 去掉字符串s右侧的空格 |
TRIM(s) | 去掉字符串s开始与结尾的空格 |
TRIM(【BOTH 】s1 FROM s) | 去掉字符串s开始与结尾的s1 |
TRIM(LEADING s1 FROM s) | 去掉字符串s开始处的s1 |
TRIM(TRAILING s1 FROM s) | 去掉字符串s结尾处的s1 |
REPEAT(str, n) | 返回str重复n次的结果 |
REPLACE(str, a, b) | 用字符串b替换字符串str中所有出现的字符串a |
STRCMP(s1,s2) | 比较字符串s1,s2 |
SUBSTRING(s,index,len) | 返回从字符串s的index位置其len个字符 |
- 举例1:大小写控制函数
函数 | 结果 |
---|---|
LOWER(‘SQL Course’) | sql course |
UPPER(‘SQL Course’) | SQL COURSE |
这类函数改变字符的大小写。
- 举例2:字符控制函数
函数 | 结果 |
---|---|
CONCAT(‘Hello’,‘World’) | HelloWorld |
SUBSTR(‘HelloWorld’,1,5) | Hello |
LENGTH(‘HelloWorld’) | 10 |
INSTR(‘HelloWorld’,‘W’) | 6 |
LPAD(salary,10,’*’) | *****24000 |
RPAD(salary,10, ‘*’) | 24000***** |
TRIM(‘H’ FROM ‘HelloWorld’) | elloWorld |
REPLACE(‘abcd’,‘b’,‘m’) | amcd |
2.5.3 数值函数
函数 | 用法 |
---|---|
ABS(x) | 返回x的绝对值 |
CEIL(x) | 返回大于x的最小整数值 |
FLOOR(x) | 返回小于x的最大整数值 |
MOD(x,y) | 返回x/y的模 |
RAND() | 返回0~1的随机值 |
ROUND(x,y) | 返回参数x的四舍五入的有y位的小数的值 |
TRUNCATE(x,y) | 返回数字x截断为y位小数的结果 |
SQRT(x) | 返回x的平方根 |
POW(x,y) | 返回x的y次方 |
- 举例1:ROUND:四舍五入
ROUND(45.926, 2) --> 45.93
- 举例2:TRUNCATE:截断
TRUNCATE(45.926) --> 45
- 举例3:MOD:求余
MOD(1600, 300) --> 100
2.5.4 日期函数
函数 | 用法 |
---|---|
CURDATE() 或 CURRENT_DATE() | 返回当前日期 |
CURTIME() 或 CURRENT_TIME() | 返回当前时间 |
NOW() / SYSDATE() / CURRENT_TIMESTAMP() / LOCALTIME() / LOCALTIMESTAMP() | 返回当前系统日期时间 |
YEAR(date) / MONTH(date) / DAY(date) / HOUR(time) / MINUTE(time) / SECOND(time) | 返回具体的时间值 |
WEEK(date) / WEEKOFYEAR(date) | 返回一年中的第几周 |
DAYOFWEEK() | 返回周几,注意:周日是1,周一是2,。。。周六是7 |
WEEKDAY(date) | 返回周几,注意,周1是0,周2是1,。。。周日是6 |
DAYNAME(date) | 返回星期:MONDAY,TUESDAY…SUNDAY |
MONTHNAME(date) | 返回月份:January,。。。。。 |
DATEDIFF(date1,date2) / TIMEDIFF(time1, time2) | 返回date1 - date2的日期间隔 / 返回time1 - time2的时间间隔 |
DATE_ADD(datetime, INTERVAL expr type) | 返回与给定日期时间相差INTERVAL时间段的日期时间 |
DATE_FORMAT(datetime ,fmt) | 按照字符串fmt格式化日期datetime值 |
STR_TO_DATE(str, fmt) | 按照字符串fmt对str进行解析,解析为一个日期 |
其中:
(1)DATE_ADD(datetime,INTERVAL expr type)
表达式类型:
参数类型 | 参数类型 |
---|---|
YEAR | YEAR_MONTH |
MONTH | DAY_HOUR |
DAY | DAY_MINUTE |
HOUR | DAY_SECOND |
MINUTE | HOUR_MINUTE |
SECOND | HOUR_SECOND |
MINUTE_SECOND |
举例:
SELECT DATE_ADD(NOW(), INTERVAL 1 YEAR);
SELECT DATE_ADD(NOW(), INTERVAL -1 YEAR); #可以是负数
SELECT DATE_ADD(NOW(), INTERVAL '1_1' YEAR_MONTH); #需要单引号
(2)DATE_FORMAT(datetime,fmt) 和 STR_TO_DATE(str, fmt)
格式符 | 说明 | 格式符 | 说明 |
---|---|---|---|
%Y | 4位数字表示年份 | %y | 表示两位数字表示年份 |
%M | 月名表示月份(January,…) | %m | 两位数字表示月份(01,02,03。。。) |
%b | 缩写的月名(Jan.,Feb.,…) | %c | 数字表示月份(1,2,3,…) |
%D | 英文后缀表示月中的天数(1st,2nd,3rd,…) | %d | 两位数字表示月中的天数(01,02…) |
%e | 数字形式表示月中的天数(1,2,3,4,5…) | ||
%H | 两位数字表示小数,24小时制(01,02…) | %h和%I | 两位数字表示小时,12小时制(01,02…) |
%k | 数字形式的小时,24小时制(1,2,3) | %l | 数字形式表示小时,12小时制(1,2,3,4…) |
%i | 两位数字表示分钟(00,01,02) | %S和%s | 两位数字表示秒(00,01,02…) |
%W | 一周中的星期名称(Sunday…) | %a | 一周中的星期缩写(Sun.,Mon.,Tues.,…) |
%w | 以数字表示周中的天数(0=Sunday,1=Monday…) | ||
%j | 以3位数字表示年中的天数(001,002…) | %U | 以数字表示年中的第几周,(1,2,3。。)其中Sunday为周中第一天 |
%u | 以数字表示年中的第几周,(1,2,3。。)其中Monday为周中第一天 | ||
%T | 24小时制 | %r | 12小时制 |
%p | AM或PM | %% | 表示% |
2.5.5 流程函数
函数 | 用法 |
---|---|
IF(value,t ,f) | 如果value是真,返回t,否则返回f |
IFNULL(value1, value2) | 如果value1不为空,返回value1,否则返回value2 |
CASE WHEN 条件1 THEN result1 WHEN 条件2 THEN result2 … [ELSE resultn] END | 相当于Java的if…else if…else… |
CASE expr WHEN 常量值1 THEN 值1 WHEN 常量值1 THEN 值1 … [ELSE 值n] END | 相当于Java的switch…case… |
SELECT employee_id,salary, CASE WHEN salary>=15000 THEN '高薪'
WHEN salary>=10000 THEN '潜力股'
WHEN salary>=8000 THEN '屌丝'
ELSE '草根' END "描述"
FROM employees;
SELECT oid,`status`, CASE `status` WHEN 1 THEN '未付款'
WHEN 2 THEN '已付款'
WHEN 3 THEN '已发货'
WHEN 4 THEN '确认收货'
ELSE '无效订单' END
FROM t_order;
- 举例1:
SELECT employee_id,12 * salary * (1 + IFNULL(commission_pct,0))
FROM employees;
- 举例2:
SELECT last_name, job_id, salary,
CASE job_id WHEN 'IT_PROG' THEN 1.10*salary
WHEN 'ST_CLERK' THEN 1.15*salary
WHEN 'SA_REP' THEN 1.20*salary
ELSE salary END "REVISED_SALARY"
FROM employees;
2.5.6 其他函数
函数 | 用法 |
---|---|
database() | 返回当前数据库名 |
version() | 返回当前数据库版本 |
user() | 返回当前登录用户名 |
password(str) | 返回字符串str的加密版本,41位长的字符串 |
md5(str) | 返回字符串str的md5值,也是一种加密方式 |
2.6 分组函数
2.6.1 组函数介绍
- 什么是分组函数
分组函数作用于一组数据,并对一组数据返回一个值。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mkacU4ZN-1571548409205)(SQL开发.assets/1554980924940.png)]
-
组函数类型
- AVG()
- SUM()
- MAX()
- MIN()
- COUNT()
2.6.2 AVG和SUM函数
可以对数值型数据使用AVG 和 SUM 函数。
#查询job_id中有REP字符串的员工工资的平均值、最大值、最小值、总数
SELECT AVG(salary), MAX(salary),MIN(salary), SUM(salary)
FROM employees
WHERE job_id LIKE '%REP%';
2.6.3 MIN和MAX函数
可以对任意数据类型的数据使用 MIN 和 MAX 函数。
SELECT MIN(salary), MAX(salary)
FROM employees;
2.6.4 COUNT函数
COUNT(*)返回表中记录总数,适用于任意数据类型。
SELECT COUNT(*)
FROM employees
WHERE department_id = 50;
•COUNT(expr) 返回expr不为空的记录总数。
SELECT COUNT(salary)
FROM employees
WHERE department_id = 50;
-
问题:用count(*),count(1)谁好呢?
其实,对于myisam引擎的表是没有区别的。这种引擎内部有一计数器在维护着行数。
Innodb引擎的表用count(*)直接读行数,效率很低,因为innodb真的要去数一遍。
2.6.5 GROUP BY
可以使用GROUP BY子句将表中的数据分成若干组
SELECT column, group_function(column)
FROM table
[WHERE condition]
[GROUP BY group_by_expression]
[ORDER BY column];
明确:WHERE一定放在FROM后面
在SELECT列表中所有未包含在组函数中的列都应该包含在 GROUP BY子句中
SELECT department_id, AVG(salary)
FROM employees
GROUP BY department_id ;
包含在 GROUP BY 子句中的列不必包含在SELECT 列表中
SELECT AVG(salary)
FROM employees
GROUP BY department_id ;
使用多个列分组
SELECT department_id dept_id, job_id, SUM(salary)
FROM employees
GROUP BY department_id, job_id ;
2.6.6 HAVING
-
非法使用组函数
- 不能在 WHERE 子句中使用组函数。
- 可以在 HAVING 子句中使用组函数。
SELECT department_id, AVG(salary)
FROM employees
WHERE AVG(salary) > 8000
GROUP BY department_id;
过滤分组:HAVING子句
- 行已经被分组。
- 使用了组函数。
- 满足HAVING 子句中条件的分组将被显示。
SELECT department_id, MAX(salary)
FROM employees
GROUP BY department_id
HAVING MAX(salary)>10000 ;
第3章:子查询
3.1 基本介绍
-
子查询 (内查询) 在主查询之前一次执行完成。
-
子查询的结果被主查询(外查询)使用 。
-
子查询的类型:
-
单行子查询
-
多行子查询
-
-
注意事项
- 子查询要包含在括号内.
- 将子查询放在比较条件的右侧.
- 单行操作符对应单行子查询,多行操作符对应多行子查询.
3.2 单行子查询
3.2.1 单行比较操作符
操作符 | 含义 |
---|---|
= | equal to |
> | greater than |
>= | greater than or equal to |
< | less than |
<= | less than or equal to |
<> | not equal to |
SELECT last_name
FROM employees
WHERE salary >
(SELECT salary
FROM employees
WHERE last_name = 'Abel');
题目:返回job_id与141号员工相同,salary比143号员工多的员工姓名,job_id和工资
SELECT last_name, job_id, salary
FROM employees
WHERE job_id =
(SELECT job_id
FROM employees
WHERE employee_id = 141)
AND salary >
(SELECT salary
FROM employees
WHERE employee_id = 143);
题目:返回公司工资最少的员工的last_name,job_id和salary
SELECT last_name, job_id, salary
FROM employees
WHERE salary =
(SELECT MIN(salary)
FROM employees);
3.2.2 子查询中的 HAVING 子句
- 首先执行子查询。
- 向主查询中的HAVING 子句返回结果。
题目:查询最低工资大于50号部门最低工资的部门id和其最低工资
SELECT department_id, MIN(salary)
FROM employees
GROUP BY department_id
HAVING MIN(salary) >
(SELECT MIN(salary)
FROM employees
WHERE department_id = 50);
3.2.3 子查询中的空值问题
SELECT last_name, job_id
FROM employees
WHERE job_id =
(SELECT job_id
FROM employees
WHERE last_name = 'Haas');
=
子查询不返回任何行
3.2.4 非法使用子查询
SELECT employee_id, last_name
FROM employees
WHERE salary =
(SELECT MIN(salary)
FROM employees
GROUP BY department_id);
多行子查询使用单行比较符
3.2 多行子查询
-
返回多行。
-
使用多行比较操作符。
操作符 | 含义 |
---|---|
IN | 等于列表中的任意一个 |
ANY | 和子查询返回的某一个值比较 |
ALL | 和子查询返回的所有值比较 |
体会any和all的区别
3.2.1 使用ANY或ALL操作符
题目:返回其它job_id中比job_id为‘IT_PROG’部门任一工资低的员工的员工号、姓名、job_id 以及salary
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ANY (SELECT salary
FROM employees
WHERE job_id = 'IT_PROG')
AND job_id <> 'IT_PROG';
题目:返回其它job_id中比job_id为‘IT_PROG’部门所有工资都低的员工的员工号、姓名、job_id以及salary
SELECT employee_id,last_name,job_id,salary
FROM employees
WHERE salary < ALL (SELECT salary
FROM employees
WHERE job_id = 'IT_PROG')
AND job_id <> 'IT_PROG';
3.3 相关子查询
相关子查询按照一行接一行的顺序执行,主查询的每一行都执行一次子查询。
题目:查询员工中工资大于本部门平均工资的员工的last_name,salary和其department_id
方式一:相关子查询
SELECT last_name,salary,department_id
FROM employees outers
WHERE salary > (SELECT AVG(salary)
FROM employees
WHERE department_id=outers.`department_id`)
方式二:在from中使用子查询
SELECT last_name,salary,e1.department_id
FROM employees e1,(SELECT department_id,AVG(salary) dept_avg_sal FROM employees GROUP BY department_id) e2
WHERE e1.`department_id` = e2.department_id
AND e2.dept_avg_sal < e1.`salary`;
题目:查询员工的id,salary,按照department_name 排序
SELECT employee_id,salary
FROM employees e
ORDER BY (
SELECT department_name
FROM departments d
WHERE e.`department_id` = d.`department_id`
);
3.4 EXISTS操作符
-
EXISTS 操作符检查在子查询中是否存在满足条件的行
-
如果在子查询中存在满足条件的行:
- 不在子查询中继续查找
- 条件返回 TRUE
-
如果在子查询中不存在满足条件的行:
- 条件返回 FALSE
- 继续在子查询中查找
题目:查询公司管理者的employee_id,last_name,job_id,department_id信息
方式一:
SELECT employee_id, last_name, job_id, department_id
FROM employees e1
WHERE EXISTS ( SELECT 'X'
FROM employees e2
WHERE e2.manager_id =
e1.employee_id);
方式二:
SELECT DISTINCT e1.employee_id, e1.last_name, e1.job_id, e1.department_id
FROM employees e1 JOIN employees e2
WHERE e1.employee_id = e2.manager_id;
方式三:
SELECT employee_id,last_name,job_id,department_id
FROM employees
WHERE employee_id IN (
SELECT DISTINCT manager_id
FROM employees
);
题目:查询departments表中,不存在于employees表中的部门的department_id和department_name
SELECT department_id, department_name
FROM departments d
WHERE NOT EXISTS (SELECT 'X'
FROM employees
WHERE department_id = d.department_id);
3.5 相关更新
UPDATE table1 alias1
SET column = (SELECT expression
FROM table2 alias2
WHERE alias1.column = alias2.column);
使用相关子查询依据一个表中的数据更新另一个表的数据。
练习:
1)
ALTER TABLE employees
ADD(department_name VARCHAR2(14));
2)
UPDATE employees e
SET department_name = (SELECT department_name
FROM departments d
WHERE e.department_id = d.department_id);
3.6 相关删除
DELETE FROM table1 alias1
WHERE column operator (SELECT expression
FROM table2 alias2
WHERE alias1.column = alias2.column);
使用相关子查询依据一个表中的数据删除另一个表的数据
题目:删除表employees中,其与emp_history表皆有的数据
DELETE FROM employees e
WHERE employee_id in
(SELECT employee_id
FROM emp_history
WHERE employee_id = e.employee_id);
今天就先到这,查询这块的内容也差不多说完了。其它的下次再说。欢迎大家一起学习交流。