doc/笔记/数据库基础SQL查询-query.md

825 lines
26 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 数据库基础SQL查询
date: 2023-04-27 11:38:38.451
updated: 2023-04-27 11:38:38.451
url: /archives/query
categories:
- 数据库
tags:
- DB
---
> 数据查询语言DQLData query language
## 基础查询
> 注意每个SQL语句后加上英文符号`;` 养成良好习惯,避免不必要问题
>
> 建议1SQL例如SELECT、FROM、WHERE等关键字使用大写
>
> 建议2所有的例如逗号、括号、引号等等符号全部使用英文符号避免不必要问题
- 查询所有列
```sql
select * from tableName;
```
- 查询指定列
```sql
select columnName1,columnName2 from tableName;
```
- 列数值计算(在列名后直接跟上运算)
```sql
select columnName1*12,columnName2 from tableName;
```
- 列字符串拼接(多列拼接作为一列显示)
```sql
-- 注意Oracle的字符串只能使用单引号
select columnName1||' '||columnName2 from tableName;
```
- 列别名
```sql
select columnName1 As alias1,columnName2 As alias2 from tableName;
```
```sql
-- As也可以省略不写
select columnName1 alias1,columnName2 alias2 from tableName;
```
- 去除重复记录
```sql
-- 在列名前使用关键字 DISTINCT 去除这列重复的记录
select DISTINCT columnName1 from tableName;
```
### 精准查询
> 条件查询使用where子句
- 基本语法
```sql
select columnName1,columnName2 from tableName where 条件表达式(可以是一个条件也可以是多个条件);
```
- 精准查询
```sql
-- 比较运算符
-- 注释在SQL的一些版本中<>可被写成!=
-- 当比较值为数值时不需要引号
-- 当比较值为字符串时必须使用单引号或者双引号
= > >= < <= != <> ^=
```
示例
```sql
-- 以工资salary大于一万作为条件执行查询
select salary from tableName where salary>10000;
```
- 范围查询 BETWEEN 下边界 AND 上边界
```sql
-- 以工资salary介于一万和两万之间作为条件执行查询
select salary from tableName where salary between 10000 and 20000;
```
- 根据多个值查询 IN值1, 值2, ... 值N
```sql
-- 以姓名username'张三','李四','王五'作为条件执行查询
select username from tableName where salary in('张三','李四','王五');
```
- 逻辑运算符
NOT 表示否定 AND 表示并且 OR 表示或者 优先级 NOT > AND > OR
逻辑运算符 not and or 优先级为 not>and>or
建议:复杂逻辑运算可以加括号避免干扰
```sql
-- 以工资等于一万或者两万作为条件执行查询
select username from tableName where salary=10000 or salary=20000;
```
- 空值比较
IS NULL 表示为空 IS NOT NULL 表示不为空
```sql
-- 以email为空作为条件执行查询
select email from tableName where email is null;
```
### 模糊查询
- like查询
两个简单的通配符:`%` 和 `_`
`%` 任意匹配,下划线 `_` 匹配一个任意字符
注意Mysql数据库支持正则表达式但是需要使用`REGEXP `关键字指定正则表达式的字符匹配模式
```sql
-- %a% a% %a 含义分别为包含字符a以字符a开头以字符a结尾以第一个举例
select name from tableName where name like '%a%';
```
### 伪列
- 关键字 ROWNUM
**注意:**并非所有的数据库系统都支持 SELECT TOP 语句。
Oracle 可以使用 ROWNUM 来选取
MySQL 可以使用 LIMIT 语句来选取指定的条数数据,
```sql
-- Oracle语法
-- 注意运算符只支持 < <=,不支持 > >= =,其中 =1 是例外支持
select columnName from tableName where rownum <= 10 ; -- 选取前10条数据
```
```sql
-- Mysql语法
select columnName from tableName LIMIT 10; -- 选取前10条数据
```
### 排序查询
排序中的空值需要扩展一下:
不同的数据库中的null在排序中的默认值是不同的
- 在`PostgreSQL`中null值默认最大
- 在`Oracle`中null值默认最大
- 在`Mysql`和`SQLServer`中null值默认最小
因此建议使用 **nulls last****nulls first** 来指定这些null值排在前面还是后面另外该关键字只能搭配order by来使用。
`ORDER BY` 子句
子句书写顺序 `SELECT -> FROM -> WHERE -> ORDER BY`
子句执行顺序 `FROM -> WHERE -> SELECT -> ORDER BY`
```sql
-- 默认就是升序排列
select salary from tableName where salary > 2000 order by;
-- 多排列先按name升序再按salary降序
select name,salary from tableName order by name asc, salary desc;
```
```sql
-- 空值排在最后
select email from tableName order by email desc nulls last;
```
### 分组查询
#### 分组函数
- 分组函数又称为统计函数,用于将查询结果分成几个组,出现与 SELECT 的列和 HAVING 子句中
- `COUNT` 统计查询出的结果数量
- `AVG` 求查询结果中某列的平均值
- `MAX` 求查询结果中某列的最大值
- `MIN` 求查询结果中某列的最小值
- `SUM` 求查询结果中某列的总和
>分组函数 AVG、SUM 只能统计数值MAX、MIN 可以统计字符串
>
>分组函数 COUNT 统计所有类型,不统计空值
>
>COUNT(*) 和 COUNT(1) 统计所有列,返回行数
>
>COUNT(字段名) 统计某字段
```sql
-- 统计表中user_id列不为空且不重复的数量
select count(distinct user_id) FROM tableName;
```
需要注意的是分组函数不能跟普通的列混合,例如下面这个错误例子:
```sql
select count(distinct user_id),name FROM tableName;
```
#### 分组查询
- `GROUP BY` 子句
- 子句书写顺序 `SELECT -> FROM -> WHERE -> GROUP BY -> ORDER BY`
- 子句执行顺序 `FROM -> WHERE -> GROUP BY -> SELECT -> ORDER BY`
- GROUP BY 子句将查询结果细分为更小的组GROUP BY 子句后只能跟字段名
- 没有 GROUP BY 子句时 SELECT 子句可以出现分组函数,但不能同时出现其它字段
- 有 GROUP BY 子句时 SELECT 子句只允许出现分组函数和分组字段
- 在 GROUP BY 子句中若出现多列时是按照多列组合值进行分组
```sql
-- 分组查询 关键字 group by + 分组的列
-- 执行顺序 from->where->group by->_func->select->order by
-- 分部门查询每个dept_id的最高salary和平均salary
select dept_id,max(salary),avg(salary)
from s_emp
group by dept_id;
-- 分部门查询每个dept_id每个title的最高工资、部门id、岗位
select max(salary) as 最高工资,dept_id as 部门id,title as 岗位
from s_emp
group by dept_id,title;-- 分组可以跟多列 先按第一列分组再按第二列分组
```
#### 分组条件过滤
- `having` 子句
- 子句书写顺序 `SELECT -> FROM -> WHERE -> GROUP BY -> HAVING -> ORDER BY`
- 子句执行顺序 `FROM -> WHERE -> GROUP BY -> HAVING -> SELECT -> ORDER BY`
- HAVING 子句用来对 GROUP BY 的结果集进行条件过滤,只允许出现分组函数和分组字段
- WHERE 子句中不允许出现分组函数HAVING 子句不允许使用列的别名
```sql
-- 分组过滤 having 子句用于对group by 子句的过滤
-- 书写顺序 select->from->where->group by->order by
-- 执行顺序 from->where->group by->分组函数->having->select->order by
-- 从员工表中查询出平均工资大于1500的部门编号、平均工资、最高工资并且按照部门编号升序排列
-- select dept_id,avg(salary),max(salary) from s_emp where avg(salary)>1500
group by dept_id; --报错 where后不能使用分组函数执行顺序
select dept_id,avg(salary),max(salary)
from s_emp
group by dept_id
having avg(salary)>1500
order by dept_id asc;
-- having 子句中跟分组字段dept_id 则与分组字段在where子句中作用是一样的
-- 从员工表中查询出部门编号大于20的部门编号、平均工资、最高工资
-- 先分组再按照分组字段对分组结果过滤
select dept_id,avg(salary),max(salary)
from s_emp
group by dept_id
having dept_id>20;
-- 先按照分组字段进行过滤 再在过滤结果上进行分组
select dept_id,avg(salary),max(salary)
from s_emp
where dept_id>20
group by dept_id;
```
### 集合操作
![](https://images.qweraq.com/images/202304271124291.webp)
#### 并集
- `UNION` 返回几个查询结果的全部内容,但不显示重复记录
- `UNION All` 返回几个查询结果的全部内容,重复记录也会显示
```
-- 并集 union 返回几个查询结果的全部内容 但不显示重复记录 union all 显示重复记录
select id,first_name from s_emp where id<8;--1 2 3 4 5 6 7
select id,first_name from s_emp where id between 6 and 10;--6 7 8 9 10
-- 1 2 3 4 5 6 7 8 9 10
select id,first_name from s_emp where id<8
union
select id,first_name from s_emp where id between 6 and 10;
-- 1 2 3 4 5 6 7 6 7 8 9 10
select id,first_name from s_emp where id<8
union all
select id,first_name from s_emp where id between 6 and 10;
```
#### 差集
- `MINUS` 返回几个查询结果的不同部分
**示例**
```plsql
--差集minus 返回几个查询结果的不同部分 从第一个集合中去掉第二个集合中的部分
--1 2 3 4 5
select id,first_name from s_emp where id<8
minus
select id,first_name from s_emp where id between 6 and 10;
```
#### 交集
- `INTERSECT` 返回几个查询结果的相同部分
**示例**
```plsql
-- 交集 intersect 取两个集合一样的行
-- 6 7
select id,first_name from s_emp where id<8
intersect
select id,first_name from s_emp where id between 6 and 10;
```
### 单行函数
#### 字符函数
Oracle SELECT SQL 语法要求一定要有数据源不需要数据源可以使用虚拟表 dual
- `UPPER` 转换成大写
- `LOWER` 转换成小写
- `INITCAP` 首字母大写
- `CONCAT(字符串1, 字符串2)` 字符串的连接
- `SUBSTR(字符串, 下标, 截取长度)` 字符串的截取下标从1开始
- `LENGTH` 获取字符串的长度
**示例**
```plsql
--虚拟表 dual
select 1+2 from dual;
--单行函数
--字符串函数
-- 将所有的职位转换成大写输出
select distinct upper(title) from s_emp; --转大写
select distinct lower(title) from s_emp; --转小写
select initcap('mark') from dual;--首字母转大写
select substr('51testing',1, 6) from dual;--字符串截取substr(字符串 起始位置 长度) 字符串下标从1开始
select substr('51testing',0,4),substr('51testing',-3, 5),substr('51testing', -3) from dual; -- 末尾的下标 -1
select concat('hello','oracle') from dual; --字符串拼接
select length('51testing') from dual; --字符串长度
--忽略大小写
--HenRy
select * from s_emp where upper(first_name)='HENRY';
select * from s_emp where lower(first_name)='henry';
```
#### 通用函数
- `NVL(列名,'字符串')` 将列中的空值替换成字符串
- `NVL(列名, 数字)` 将列中的空值替换成数值
**示例**
```plsql
--通用函数 NVL(列名,'字符串')NVL(列名,数值) 列名中值为空的使用后面的数据补齐
select salary, salary+salary*commission_pct/100 from s_emp;
select salary, salary+salary*nvl(commission_pct,0)/100 from s_emp;
```
#### 数值函数
- `ROUND` 四舍五入
- `TRUNC` 截取不进行四舍五入
- `MOD` 取余
**示例**
```plsql
--round(字段,数值)四舍五入 数值可以不传表示取整 为正表示小数点后保留几位 为负表示小数点前向前取整取几位
select round(1234.567),round(1234.567,1),round(1234.567,-2) from dual; --1235 1234.6 1200
--trunc(字段,数值) 截位
select trunc(1234.567), trunc(1234.567,1),trunc(1234.567,-2) from dual; --1234 1234.5 1200
--mod(数值1,数值2) 取余
select mod(5,2) from dual; --1
```
#### 转换函数
- `TO_NUMBER(字符串)` 将一个数值字符串转换成数值
**示例**
```plsql
-- 转换函数
-- to_number(字符串)
select to_number('1234.567') from dual;
```
- `TO_CHAR(数值,'格式')` 数值转换成字符串
> **格式**
> 0显示数字如位数不足则用0补齐。9显示数字并忽略前面0。逗号是千分号。L表示本地货币符号$表示美元符号。
> 格式最前面加FM可以删除9占位所产生的多余空格或0
**示例**
```plsql
--to_char(数值'格式') --占位符 0 显示数字如果位数不足使用0补齐 9表示占位 如果位数不足用0补齐并且忽略小数点前面的0
select to_char(1234567.5678,'99,999,999.999'),to_char(1234567.5678,'00,000,000.000') from dual; -- 逗号表示千分位
--FM 可以删除 9 占位 小数点后的占位 对格式0占位无效的
--L表示本地货币 $表示美元
select to_char(1234567.5678, 'FML99,999,999.999999999'),to_char(1234567.5678, 'FML00,000,000.000000000') from dual;
```
- `TO_CHAR(日期,'格式')` 日期转换成字符串
> **日期格式模型元素**
> YYYY 四位数字的年 YY 两位数字的年
> MM 两位数字的月 MON 月份的缩写 MONTH 月份的全称
> DD 一个月的第几天 DDD 一年的第几天
> DAY 星期几的全称 DY 星期几的缩写 D 星期几的数字表示星期天为1星期六为7
> HH 12小时制 HH24 24小时制 MI 分钟 SS 秒 AM 上午 PM 下午
> **日期的文字方式显示结果和日期语言相关**
> ALTER SESSION SET NLS_DATE_LANGUAGE="语言";
**示例**
```plsql
-- to_char(日期'格式')
select start_date from s_emp;
alter session set nls_date_language='simplified chinese';--- 设置日期显示的语言格式
alter session set nls_date_language='english';--- 设置日期显示的语言格式
--YYYY四位数字年 MM两位数字月DD表示一个月第几天 HH时MI分SS秒 HH24 24小时制
select to_char(start_date, 'YYYY/MM/DD HH24:MI:SS') from s_emp;
--MON 表示月份的简写 DD 表示两位天 YY 表示两位年
select to_char(sysdate, 'mon-dd-yy') from dual;
--ddd 表示是一年的第几天 day 表示星期几 dy 星期几的简写 d 表示一周的第几天 会将星期日作为第一天
select to_char(sysdate, 'yyyy.ddd'),--ddd 表示是一年的第几天
to_char(sysdate,'day'),--day 表示星期几
to_char(sysdate, 'dy'),--dy 星期几的简写
to_char(sysdate,'d'),--d 表示一周的第几天 会将星期日作为第一天
to_char(sysdate,'yyyy/mm/dd hh:mi:ss pm'), --pm 展示上午下午
to_char(sysdate, 'mon-dd-yy') -- MON 表示月份的简写 DD 表示两位天 YY 表示两位年
to_char(sysdate, 'month') --月份全称
from dual;
```
- `TO_DATE(字符串,'格式')` 将一个日期字符串转换成日期日期可以进行加减不能进行乘除
**示例**
```plsql
select to_date('2023/3/17','yyyy/mm/dd')-30 from dual;--返回计算后的日期
```
==**练习**==
```plsql
-- 从订单表s_ord中查询出编号sales_rep_id为11销售代表的每个订单金额total.
-- 金额以美元显示整数7位小数两位小数不足的位数以0填充有千分位
SELECT total, TO_CHAR(total, '$9,999,999.00'), TO_CHAR(total, 'FM$9,999,999.00')
FROM s_ord
WHERE sales_rep_id = 11;
-- 从员工表s_emp中查询出1991年入职的员工姓名和入职日期输出日期格式为数字形式的年/月其中年为4位数字。
SELECT first_name||' '||last_name 姓名, TO_CHAR(start_date, 'YYYY/MM') 入职日期
FROM s_emp
WHERE TO_CHAR(start_date, 'YYYY') = '1991';
```
### 多表查询
#### 数据库范式
- 第一范式要求表中无重复的列
- 第二范式需满足第一范式要求表中无重复的行
- 第三范式需满足第二范式要求一个数据库表中不包含已在其它表中已包含的非主关键字信息
`Primary Key` 主键 用来唯一标识表中的每一行数据
`Foreign Key` 外键 表与表之间的关联关系
#### 等值连接
- 判断某两列是否相等从而建立连接通常是主键与外键进行连接
- 采用表名加上列名来标示列名从而防止出现列名相同的情况
- 表可以起别名表的别名前不能加AS关键字
- 一旦给表起了别名标识列时只能通过表的别名标识而不能通过原名标识
**示例**
```plsql
-- 多表查询
--等值连接
-- 查询所有员工的名字职位和部门名称
-- 找到数据源 s_emp s_dept
-- 找到关联关系 s_emp dept_id 和s_dept id s_emp dept_id = s_dept id
-- 找到我们要查询的列
-- 可以给表起别名 通过表名.的方式使用 如果表有别名 就不再使用原先表名
-- 列名有冲突的时候使用表名.列名的方式区分 表名 的别名不使用 as
select * from s_emp;
select * from s_dept;
select * from s_emp,s_dept;--笛卡儿积 集合A 1 2 3 集合B a b c <1,a><1,b><1,c><2,a><2,b><2,c><3,a><3,b><3,c>
select * from s_emp,s_dept where s_emp.dept_id=s_dept.id; -- 关联关系
select first_name, title, name from s_emp,s_dept where s_emp.dept_id=s_dept.id;--找到查询的列
-- 查询部门是41的员工的名字、职位和部门名称
-- 找到数据源 s_emp s_dept
-- 对应关系 s_emp.dept_id=s_dept.id
-- 找列
select a.id,a.first_name,a.title,b.name,b.id
from s_emp a,s_dept b
where a.dept_id=b.id and a.dept_id=41;
```
#### 内连接
- `INNER JOIN` 内连接
用INNER JOIN连接表关联条件写在ON后面INNER可省略
从多个表中返回满足关联条件的所有行
![](https://images.qweraq.com/images/202304271130774.webp)
**示例**
```plsql
--内连接 表1inner join 表2 on 条件 其中inner可以省略
-- 查询部门为41的所有员工的名字、职位、部门名称
select first_name,title,name
from s_emp a
join s_dept b
on a.dept_id=b.id
where a.dept_id=41;
-- 查询部门为41的所有员工名字、部门名称、地区名称
-- 数据源 s_emp s_dept s_region
-- 关联关系 s_emp.dept_id s_dept.id s_dept.region_id s_region.id
--
select a.first_name 员工名字,b.name 部门名称, c.name 地区名称
from s_emp a
join s_dept b
on a.dept_id=b.id
join s_region c
on b.region_id=c.id
where a.dept_id=41;
--自连接
--查询部门41的员工的名字、职位和主管名字
-- 数据源 s_emp a s_emp b
-- a.manager_id=b.id
select a.first_name, a.title, b.first_name
from s_emp a
join s_emp b
on a.manager_id=b.id
where a.dept_id=41;
```
#### 左外连接
- `LEFT OUTER JOIN` 左外连接
从左表返回所有的行,即使右表中没有匹配。如果右表中没有匹配,则结果为 NULL。OUTER可省略。
![](https://images.qweraq.com/images/202304271131588.webp)
**示例**
```plsql
--左外连接 left outer join on outer可以省略
-- 以左表为准左表中所有的数据都会被展示如果在右表中没有匹配的数据也展示右表数据展示为null
--查询员工的姓名、职位和客户姓名
--数据源 s_emp s_customer
--关系 s_emp.id s_customer.sales_rep_id
select a.first_name,a.title,b.name
from s_emp a, s_customer b
where a.id=b.sales_rep_id;
select a.first_name,a.title,b.name
from s_emp a
left outer join s_customer b
on a.id=b.sales_rep_id;
```
#### 右外连接
- `RIGHT OUTER JOIN` 右外连接
从右表返回所有的行,即使左表中没有匹配。如果左表中没有匹配,则结果为 NULL。OUTER可省略。
![](https://images.qweraq.com/images/202304271132495.webp)
**示例**
```plsql
-- 右外连接 以右表为准返回所有行 即使左表中查询不到相关数据 仍然显示但是有表数据显示为Null
--right outer join on outer可以省略
select a.first_name,a.title,b.name
from s_emp a
right outer join s_customer b
on a.id=b.sales_rep_id;
```
#### 全外连接
- `FULL OUTER JOIN` 全外连接
只要左表和右表其中一个表中存在匹配则返回行。OUTER可省略。
![](https://images.qweraq.com/images/202304271132236.webp)
**示例**
```plsql
--全外连接
-- full outer join on 其中 outer 可以省略
-- 只要左表和右表中其中有一个存在匹配 返回该行
select a.first_name,a.title,b.name
from s_emp a
full join s_customer b
on a.id=b.sales_rep_id;
```
### 子查询(嵌套查询)
- 子查询是一个完整的 SELECT 语句,可以拥有 GROUP BY、HAVING 子句,可以使用组函数,可以从多个表查询结果
- 子查询一般不包含 ORDER BY 子句除非需要进行排名前几位的查询Top-N
- 如果子查询的外围语句是 SELECT 语句,则子查询内容必须用小括号界定,子查询里面不用分号结尾
- 子查询的作用:
1. 方便理解
2. 实现更复杂的查询
3. 提高查询效率
- 当直接查询某些数据很困难或办不到时,可以通过从“查询结果集”中再次提取数据集来实现复合查询。
- 子查询的位置通常出现在 WHERE 子句、HAVING 子句、FROM 子句
#### WHERE 子句单行子查询
- 子查询返回结果是单行单列,使用单行比较符 `= != > >= < <=`
**示例**
```plsql
-- where 子查询
-- 单行子查询(单行单列)
-- 查询最低工资的员工姓名、职位、工资
--步骤1找到最低工资
select min(salary) from s_emp; -- 750
--步骤2找到对应的员工姓名、职位、工资
select first_name,title,salary from s_emp where salary=750;
-- 步骤3做子查询替换
select first_name,title,salary from s_emp where salary=(select min(salary) from s_emp);
```
#### WHERE 子句多行子查询
- 子查询返回多行单列,使用多行比较符 `IN ANY ALL`
>IN 等于列表中的任意一个
>ALL 和子查询返回的所有值比较(大于最大的,小于最小的)
>ANY 和子查询返回的任意一个值比较(大于最小的,小于最大的)
**示例**
```plsql
-- 多行子查询(多行单列) in all any
-- 查询部门名称是Sales的部门所有员工姓名、职位
--1.找到name是Sales的部门 的部门编号
select id from s_dept where name='Sales';-- [31,32,33,34,35]
--2.查找部门编号dept_id是 [31,32,33,34,35] 的姓名和职位
select first_name,title from s_emp where dept_id in (31,32,33,34,35);
--3.做子查询语句替换
select first_name,title from s_emp where dept_id in (select id from s_dept where name='Sales');
-- all 跟子查询中所有的值作比较 >all 大于最大值 <all小于最小值
-- 查询部门编号比Sales部门都小的部门的所有员工的姓名和职位
select id from s_dept where name='Sales';--[31,32,33,34,35]
select first_name,title from s_emp where dept_id<all(select id from s_dept where name='Sales');
-- any 跟子查询中所有的值作比较 >any 大于最小值 <any 小于最大值
-- 查询部门编号比Sales部门中其中一个大的部门的所有员工姓名和职位
select first_name,title from s_emp where dept_id>any(select id from s_dept where name='Sales');
```
#### HAVING 子句子查询
- 子查询返回结果是单行单列或多行单列,也可以使用在 HAVING 子句
**示例**
```plsql
--Having 子查询
-- 查询部门平均工资比公司平均工资高的部门编号
-- 1.查询公司的平均工资
select avg(salary) from s_emp; --1255.08
-- 2.查询部门的平均工资
select dept_id, avg(salary)
from s_emp
group by dept_id
having avg(salary)>1255.08;
-- 3.替换
select dept_id, avg(salary)
from s_emp
group by dept_id
having avg(salary)>(select avg(salary) from s_emp);
```
#### FROM 子句子查询
- 子查询返回结果是多行多列,可以使用在 FROM 子句作为数据源
**示例**
```plsql
-- from 子查询(数据源)
-- 子查询的结果是多行多列
-- 查询工资从高到低排名前5的员工名字和职位
select first_name,title
from s_emp
where rownum<6 order by salary desc; -- 执行顺序 from->where->select->order by
-- 1.找到员工的姓名和职位 工资从高到低
select first_name,title from s_emp order by salary desc; --子查询的结果 是一个两列(first_name,title)的临时表
-- 2.截取前5个
select * -- 可以用*获取数据源所有列即first_name,title
from (select first_name,title from s_emp order by salary desc) -- 子查询的结果作为数据源
where rownum<6;
```
#### 子查询和多表查询结合
- 多行多列的子查询结果可以作为多表查询的数据源
**示例**
```plsql
-- 多表查询和子查询相结合
-- 多行多列的子查询结果可以作为多表查询的数据源
-- 子查询产生的表如果列有分组函数 必须加别名
-- 查询出每个部门名称最高工资和人数
--1.数据源 s_dept a s_emp b
-- 部门的编号最高工资 和人数
select dept_id,max(salary),count(*) from s_emp group by dept_id;
--2.关系 a.id b.dept_id
select a.name 部门名称, c.maxsal 最高工资, c.count 人数
from s_dept a
left join (select dept_id deptid,max(salary) maxsal,count(*) count from s_emp group by dept_id) c
on a.id=c.deptid;
```
#### 子查询嵌套
- 子查询可以多层嵌套
**示例**
```plsql
-- 子查询嵌套
--查询工资比Henry所在部门平均工资高的所有员工的名字职位工资
--1 求henry所在部门id
select dept_id from s_emp where first_name='Henry';
--2 部门的平均工资
select avg(salary) from s_emp where dept_id=(select dept_id from s_emp where first_name='Henry');
--3 以2为条件 过滤 取字段
select first_name,title,salary
from s_emp
where salary>(select avg(salary) from s_emp where dept_id=(select dept_id from s_emp where first_name='Henry'));
```
==**练习**==
```plsql
-- 从订单表s_ord中查询出比销售代表编号sales_rep_id为11的所有订单金额total都低的订单信息
SELECT * FROM s_ord WHERE total < (SELECT MIN(total) FROM s_ord WHERE sales_rep_id=11);
```