--- title: 数据库基础SQL查询 date: 2023-04-27 11:38:38.451 updated: 2023-04-27 11:38:38.451 url: /archives/query categories: - 数据库 tags: - DB --- > 数据查询语言DQL:Data query language ## 基础查询 > 注意:每个SQL语句后加上英文符号`;` 养成良好习惯,避免不必要问题 > > 建议1:SQL例如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 大于最大值 any 大于最小值 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); ```