关联查询
- 一、sql:1992语法的连接
- 笛卡尔积
- 等值连接
- 非等值连接
- 自连接
- 外连接
- 二、sql:1999语法的连接
- 交叉连接
- 自然连接
- USING创建连接
- ON创建连接
- 左外连接
- 右外连接
- FULL OUTER JOIN
- INNER JOIN
- 三、子查询
- 子查询的种类
- 单行子查询
- 多行子查询
- 在From字句中使用子查询
- 练习
- 四、行转列
一、sql:1992语法的连接
- 语法规则
SELECT tables1.column,tables2.column
FROM tables1,tables2
WHERE tables1.column1 = tables2.column2; - 连接的类型
等值连接:Equi join
非等值连接:Non-equi join
外连接:Outer join
自连接:Self join
注意:
1、在where子句中写入连接条件。
2、当多个表中有重名列时,必须在列的名字前加入表名作为前缀。
92语法: 数据来自于多张表。
注意: 明确引用同名的列,必须使用表名或者别名区分。
- 笛卡尔积
select 字段列表 from 表1,表2,表3… - 等值连接:去关系列相同的记录
select 字段列表 from 表1,表2,表3… where 表1.列 = 表2.列 and 表1.列 = 表3.列; - 非等值连接:取关系列不同的记录 != > < >= <= between and
select 字段列表 from 表1,表2,表3… where 表1.列 != 表2.列 and 表1.列 != 表3.列; - 自连接:(特殊的等值连接)列来自于同一张表,不同角度看待表
select 字段列表 from 表1 e,表1 m where e.列1 = m.列2; - 外连接:在等值的基础上,确保一张表(主表)的记录都存在从表满足则匹配,不满足补充null。
(1)左外:主表在左边
(2)右外:主表在右边
笛卡尔积
笛卡尔积: 当关联多张表,但是不指定连接条件的时候,会进行笛卡尔积,关联后的总记录条数为M * N,一般不要使用。
select * from emp e,dept d;
等值连接
等值连接: 两个表中包含相同的列名。
- 语法规则
SELECT table1.column,table2.column
FROM table1,table2
WHERE table1.column1 = table2.column2; - 笛卡尔积:表*表
- 主外键
1、在外键表中的映射字段称为外键 Foreign key
2、在主键表中的唯一字段称为主键 Primary key
例: 查询雇员的名称和部门的名称
select ename,dname from emp,dept where emp.deptno = dept.deptno;
非等值连接
非等值连接: 两个表中没有相同的列名,但是某一个列在另一张表的列的范围之中;<,>,<=,>=,!=,between,and连接时称为非等值连接。
- 语法规则
SELECT table1.column,table2.column
FROM table1,table2
WHERE table1.column1 != table2.column2;
**例:**查询雇员名称以及自己的薪水等级
select e.ename,e.sal,sg.grade from emp e,salgrade sg where e.sal between sg.losal and sg.hisal;
自连接
自连接: 将一张表1当成不同的表来看待,自己关联自己。
**例:**将雇员和他经理的名称查出来。
select e.ename,m.ename from emp e,emp m where e.mgr = m.empno;
外连接
外连接: 在等值的基础上,确保一张表(主表)的记录都存在从表满足则匹配,不满足补充null。
例: 需要将雇员表中的所有数据都进行显示。
分析:利用等值连接的话会把关联到的数据显示,没有关联到的数据不会显示,此时需要外连接。
select * from emp e,dept d where e.deptno = d.deptno; --等值连接
select * from emp e,dept d where e.deptno = d.deptno(+); --左外连接
select * from emp e,dept d where e.deptno(+) = d.deptno; --右外连接
我们学习完92语法可以明显发现92的表连接语法的问题,在92语法中,多张表的连接条件会放在where子句中,同时where需要对表进行过滤,因此,相当于将过滤条件和连接条件揉到一起,太乱了,因此出现了99语法。
二、sql:1999语法的连接
SQL1999修正了上述的问题,吧连接条件,过滤条件分开来,包括以下新的TABLE JOIN句法机构:
- CROSS JOIN
- NATURAL JOIN
- USING子句
- ON子句
- LEFT OUTER JOIN
- RIGHT OUTER JOIN
- FULL OUTER JOIN
- INNER JOIN
交叉连接
CROSS JOIN: 等同于92语法中的笛卡尔积。
例: select * from emp cross join dept;
自然连接
NATURAL JOIN: 相当于是等值连接,但是注意,不需要写连接条件,会从两张表中找到相同的列做连接,当两张表中不具有相同的列名的时候,会进行笛卡尔积操作,自然连接跟92语法的自连接没有任何关系。
例:
select * from emp e natural join dept d;
select * from emp e natural join salgrade sg;
注意: 自然连接的结果不保留重复的属性。
USING创建连接
USING子句: 可以使用using作为连接条件。
例:
select deptno from emp e join dept d using(deptno);
select e.deptno,d.deptno from emp e join dept d on e.deptno = d.deptno;
ON创建连接
ON子句: 可以添加任意的连接条件。
1、自然连接的条件是基于表中所有同名列的等值连接。
2、为了设置任意的连接条件或者指定连接的列,需要使用ON子句。
3、连接条件与其它的查询条件分开书写。
4、使用ON子句使查询语句更容易理解。
例:
相当于92语法中的等值连接
select * from emp e join dept d on e.deptno = d.deptno;
相当于92语法中的非等值连接
select * from emp e join salgrade sg on e.sal between sg.losal and sg.hisal;
左外连接
LEFT OUTER JOIN: 会把左表中的全部数据正常显示,右表没有对应数据直接显示空即可。
例: select * from emp e left outer join dept d on e.deptno = d.deptno;
右外连接
RIGHT OUTER JOIN: 会把右表中的全部数据正常显示,左表中没有对应的记录的话显示空即可。
例: select * from emp e right outer join dept d on e.deptno = d.deptno;
FULL OUTER JOIN
FULL OUTER JOIN: 相当于左外连接和右外连接的合集。
例: select * from emp e full outer join dept d on e.deptno = d.deptno;
INNER JOIN
INNER JOIN: 两张表的连接查询,只会查询出有匹配记录的数据。
例: select * from emp e inner join dept d on e.deptno = d.deptno;
三、子查询
概念: SQL允许多层嵌套。子查询,即嵌套在其它查询中的查询。
- 语法规则
SELECT select_list
FROM table
WHERE expr operator (SELECT select_list FROM table);
理解子查询的关键在于把子查询当作一张表来看待。 外层的语句可以把内嵌的子查询返回的结果当成一张表使用。
1、子查询要用括号括起来。
2、将子查询放在比较运算符的右边。(增强可读性)
子查询的种类
- 按照子查询返回的记录数,子查询可以分为单行子查询和多行子查询。
单行子查询
- 子查询返回一条记录。
- 使用单行记录比较运算符。
Operator | Meaning |
---|---|
= | Equal to |
> | Greater than |
>= | Greater than or equal to |
< | Less than |
<= | Less than or equal to |
<> | Not equal to |
例: 有哪些人的薪水是在整个雇员的平均薪水之上?
1、先求平均薪水
select avg(e.sal) from emp e;
2、把所有人的薪水与平均薪水比较
select * from emp e where e.sal > (select avg(e.sal) from emp e);
注意:此处嵌套的子查询在外层查询处理之前执行。
多行子查询
- 子查询返回多行行记录。
- 使用集合比较运算符。
运算符 | 含义 |
---|---|
IN | 等于列表中的任何值 |
some | 将值与子查询返回的任意一个值进行比较 |
ALL | 比较子查询返回的每一个值 |
- 在多行子查询中使用in
例: 我们要查在雇员中有哪些人是经理人?
1、查询所有经理人编号
select distinct e.mgr from emp e;
2、在雇员表中过滤这些编号即可
select * from emp e where e.empno in (select distinct e.mgr from emp e);
- 在多行子查询中使用some all
例: 找出部门编号为20的所有员工中收入最高的职员?
1、找出部门编号为20的所有员工的收入
select sal from emp where deptno = 20;
2、比较这些员工的工资找出最高工资的职员
select * from emp where sal >= all(select sal from emp where deptno = 20) and deptno = 20;
在From字句中使用子查询
例: 求每个部门平均薪水的等级?
1、先求出部门的平均薪水
select e.deptno,avg(e.sal) from emp e group by e.deptno;
2、跟薪水登记表做关联,求出平均薪水的等级
select t.deptno, sg.grade from salgrade sg join (select e.deptno, avg(e.sal) vsal from emp e group by e.deptno) t on t.vsal between sg.losal and sg.hisal;
练习
- 题1:求平均薪水最高的部门的部门编号
1、求部门的平均薪水
select e.deptno,avg(e.sal) from emp e group by e.deptno;
2、求平均薪水最高的部门
select max(t.vsal) from (select e.deptno, avg(e.sal) vsal from emp e group by e.deptno) t;
3、求部门编号
select t.deptno from (select e.deptno, avg(e.sal) vsal from emp e group by e.deptno) t where t.vsal = (select max(t.vsal) from (select e.deptno, avg(e.sal) vsal from emp e group by e.deptno) t);
- 题2:求部门平均的薪水等级
1、求部门每个人的薪水等级
select e.deptno,sg.grade from emp e join salgrade sg on e.sal between sg.losal and sg.hisal;
2、按照部门求平均薪水等级
select t.deptno, avg(t.grade) from (select e.deptno, sg.grade from emp e join salgrade sg on e.sal between sg.losal and sg.hisal) t group by t.deptno;
- 题3:求薪水最高的前5名雇员
select * from (select * from emp order by sal desc) t where rownum <= 5;
注意:限制输出,limit是mysql中用来做限制输出的,但是oracle中不是;在Oracle中,如果需要使用限制输出和分页的功能的话,必须要使用rownum,但是rownum不能直接使用,需要嵌套使用。 - 题4:求薪水最高的第6到10名雇员
select t.,rownum from (select * from emp order by sal desc) t where rownum <= 10;
方式一:select * from (select t., rownum rn from (select * from emp order by sal desc) t where rownum <= 10) t where t.rn > 5 and t.rn <= 10;
方式二:select * from (select t.*, rownum rn from (select * from emp order by sal desc) t) t where t.rn > 5 and t.rn <= 10;
注意:使用rownum的时候必须要在外层添加嵌套,此时才能将rownum作为其中的一个列,然后在进行限制输出。
四、行转列
例1:
1、建表
create table tmp(rq varchar2(10),shengfu varchar2(5));
insert into tmp values(‘2005-05-09’,‘胜’);
insert into tmp values(‘2005-05-09’,‘胜’);
insert into tmp values(‘2005-05-09’,‘负’);
insert into tmp values(‘2005-05-09’,‘负’);
insert into tmp values(‘2005-05-10’,‘胜’);
insert into tmp values(‘2005-05-10’,‘负’);
insert into tmp values(‘2005-05-10’,‘负’);
2、要求
日期 | 胜 | 负 |
---|---|---|
2005-05-09 | 2 | 2 |
2005-05-10 | 1 | 2 |
3、解答
select rq,decode(shengfu,‘胜’,1) 胜,decode(shengfu,‘负’,1) 负 from tmp;
select rq,count(decode(shengfu, ‘胜’, 1)) 胜,count(decode(shengfu, ‘负’, 1)) 负 from tmp group by rq;
例2:
1、建表
create table STUDENT_SCORE(name VARCHAR2(20),subject VARCHAR2(20),score NUMBER(4,1));
insert into student_score (NAME, SUBJECT, SCORE) values (‘张三’, ‘语文’, 78.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘张三’, ‘数学’, 88.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘张三’, ‘英语’, 98.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘李四’, ‘语文’, 89.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘李四’, ‘数学’, 76.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘李四’, ‘英语’, 90.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘王五’, ‘语文’, 99.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘王五’, ‘数学’, 66.0);
insert into student_score (NAME, SUBJECT, SCORE) values (‘王五’, ‘英语’, 91.0);
2、要求
姓名 | 语文 | 数学 | 英语 |
---|---|---|---|
张三 | 78 | 88 | 98 |
李四 | 89 | 76 | 90 |
王五 | 99 | 66 | 91 |
至少四种方式解答。
3、解答
方式一:decode
select ss.name,max(decode(ss.subject, ‘语文’, ss.score)) 语文,max(decode(ss.subject, ‘数学’, ss.score)) 数学,max(decode(ss.subject, ‘英语’, ss.score)) 英语 from student_score ss group by ss.name;
方式二:case when
select ss.name,max(case ss.subject when ‘语文’ then ss.score end) 语文,max(case ss.subject when ‘数学’ then ss.score end) 数学,max(case ss.subject when ‘英语’ then ss.score end) 英语 from student_score ss group by ss.name;
方式三:join
select ss.name,ss.score 语文 from student_score ss where ss.subject = ‘语文’;
select ss.name,ss.score 数学 from student_score ss where ss.subject = ‘数学’;
select ss.name,ss.score 英语 from student_score ss where ss.subject = ‘英语’;
select ss01.name, ss01.score 语文, ss02.score 数学, ss03.score 英语 from (select ss.name, ss.score from student_score ss where ss.subject = ‘语文’) ss01 join (select ss.name, ss.score from student_score ss where ss.subject = ‘数学’) ss02 on ss01.name = ss02.name join (select ss.name, ss.score from student_score ss where ss.subject = ‘英语’) ss03 on ss01.name = ss03.name;
方式四:union all
select t.name, sum(t.语文), sum(t.数学), sum(t.英语) from (select ss01.name, ss01.score 语文, 0 数学, 0 英语 from student_score ss01 where ss01.subject = ‘语文’ union all select ss02.name, 0 语文, ss02.score 数学, 0 英语 from student_score ss02 where ss02.subject = ‘数学’ union all select ss03.name, 0 语文, 0 数学, ss03.score 英语 from student_score ss03 where ss03.subject = ‘英语’) t group by t.name;