连接查询
连接查询主要分为三种:内连接、外连接、交叉连接
交叉连接
交叉连接即笛卡儿乘积,是指两个关系中所有元组的任意组合。一般情况下,交叉查询是没有实际意义的。
例如:如果希望得到学生表和选课表两个关系模式的乘积,查询语句为
SELECT*
FROM学生表CROSS JOIN选课表
内连接
内连接是一种最常用的连接类型。内连接查询实际上是一种任意条件的查询。使用内连接时,如果两个表的相关字段满足连接条件,就从这两个表中提取数据并组合成新的记录,也就是在内连接查询中,只有满足条件的元组才能出现在结果关系中。
例如:要查询每个已经选课的学生的情况,查询语句为
SELECT*
FROM学生表INNER JOIN选课表ON学生表.学号=选课表.学号
分类:
根据比较方式分为:
1)等值连接:在连接条件中使用等于号(=)运算符比较被连接列的列值,其查询结果中列出被连接表中的所有列,包括其中的重复列。
2)不等连接:在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。这些运算符包括>、>=、<=、<、!>、!<和<>。
3)自然连接:在连接条件中使用等于(=)运算符比较被连接列的列值,但它使用选择列表指出查询结果集合中所包括的列,并删除连接表中的重复列。
自连接
如果在一个连接查询中,涉及到的两个表都是同一个表,这种查询就称为自连接查询。同一张表在FROM字句中多次出现,为了区别该表的每一次出现,需要为表定义一个别名。自连接是一种特殊的内连接,它是指相互连接的表在物理上为同一张表,但可以在逻辑上分为两张表。
例如:要求检索出学号为20210的学生的同班同学的信息,查询语句为
SELECT学生表.*
FROM学生表JOIN学生表AS学生表1ON学生表.班级=学生表1.班级
WHERE学生表1.学号='20210'
外连接
内连接的查询结果都是满足连接条件的元组。但有时我们也希望输出那些不满足连接条件的元组信息。比如,我们想知道每个学生的选课情况,包括已经选课的学生(这部分学生的学号在学生表中有,在选课表中也有,是满足连接条件的),也包括没有选课的学生(这部分学生的学号在学生表中有,但在选课表中没有,不满足连接条件),这时就需要使用外连接。外连接是只限制一张表中的数据必须满足连接条件,而另一张表中的数据可以不满足连接条件的连接方式。3种外连接:
1)左外连接(LEFTOUTER JOIN)
如果在连接查询中,连接管子左端的表中所有的元组都列出来,并且能在右端的表中找到匹配的元组,那么连接成功。如果在右端的表中,没能找到匹配的元组,那么对应的元组是空值(NULL)。这时,查询语句使用关键字LEFT OUTERJOIN,也就是说,左外连接的含义是限制连接关键字右端的表中的数据必须满足连接条件,而不关左端的表中的数据是否满足连接条件,均输出左端表中的内容。
例如:要查询所有学生的选课情况,包括已经选课的和还没有选课的学生,查询语句为
SELECT学生表.学号,姓名,班级,课程号,成绩
FROM学生表LEFT OUTER JOIN选课表ON学生表.学号=选课表.学号
左外连接查询中左端表中的所有元组的信息都得到了保留。
2)右外连接(RIGHTOUTERJOIN)
右外连接与左外连接类似,只是右端表中的所有元组都列出,限制左端表的数据必须满足连接条件,而不管右端表中的数据是否满足连接条件,均输出表中的内容。
例如:同上例内容,查询语句为
SELECT学生表.学号,姓名,班级,课程号,成绩
FROM学生表RIGHTOUTERJOIN选课表ON学生表.学号=选课表.学号
右外连接查询中右端表中的所有元组的信息都得到了保留。
3)全外连接(FULL OUTER JOIN)
全外连接查询的特点是左、右两端表中的元组都输出,如果没能找到匹配的元组,就使用NULL来代替。
例如:同左外连接例子内容,查询语句为
SELECT学生表.学号,姓名,班级,课程号,成绩
FROM学生表FULL OUTER JOIN选课表ON学生表.学号=选课表.学号
全外连接查询中所有表中的元组信息都得到了保留。
从网上找了个练习题,练一哈手。
有两张表dept和emp,如下图
dept
emp
练习题
-- 1.列出至少有一个员工的所有部门。
-- 2.列出薪金比"刘一"多的所有员工。
-- 3.***** 列出所有员工的姓名及其直接上级的姓名。
-- 4.列出受雇日期早于其直接上级的所有员工。
-- 5.列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门。
-- 6.列出所有job为“职员”的姓名及其部门名称。
-- 7.列出最低薪金大于1500的各种工作。
-- 8.列出在部门 "销售部" 工作的员工的姓名,假定不知道销售部的部门编号。
-- 9.列出薪金高于公司平均薪金的所有员工。
-- 10.列出与"周八"从事相同工作的所有员工。
-- 11.列出薪金等于部门30中员工的薪金的所有员工的姓名和薪金。
-- 12.列出薪金高于在部门30工作的所有员工的薪金的员工姓名和薪金。
-- 13.列出在每个部门工作的员工数量、平均工资。
-- 14.列出所有员工的姓名、部门名称和工资。
-- 15.列出所有部门的详细信息和部门人数。
-- 16.列出各种工作的最低工资。
-- 17.列出各个部门的 经理 的最低薪金。
-- 18.列出所有员工的年工资,按年薪从低到高排序。
-- 19.查出emp表中薪水在3000以上(包括3000)的所有员工的员工号、姓名、薪水。
-- 20.查询出所有薪水在'陈二'之上的所有人员信息。
-- 21.查询出emp表中部门编号为20,薪水在2000以上(不包括2000)的所有员工,显示他们的员工号,姓名以及薪水,以如下列名显示:员工编号 员工名字 薪水
-- 22.查询出emp表中所有的工作种类(无重复)
-- 23.查询出所有奖金(comm)字段不为空的人员的所有信息。
-- 24.查询出薪水在800到2500之间(闭区间)所有员工的信息。(注:使用两种方式实现and以及between and)
-- 25.查询出员工号为7521,7900,7782的所有员工的信息。(注:使用两种方式实现,or以及in)
-- 26.查询出名字中有“张”字符,并且薪水在1000以上(不包括1000)的所有员工信息。
-- 27.查询出名字第三个汉字是“多”的所有员工信息。
-- 28.将所有员工按薪水升序排序,薪水相同的按照入职时间降序排序。
-- 29.将所有员工按照名字首字母升序排序,首字母相同的按照薪水降序排序。 order by convert(name using gbk) asc;
-- 30.查询出最早工作的那个人的名字、入职时间和薪水。
-- 31.显示所有员工的名字、薪水、奖金,如果没有奖金,暂时显示100.
-- 32.显示出薪水最高人的职位。
-- 33.查出emp表中所有部门的最高薪水和最低薪水,部门编号为10的部门不显示。
-- 34.删除10号部门薪水最高的员工。
-- 35.将薪水最高的员工的薪水降30%。
-- 36.查询员工姓名,工资和 工资级别(工资>=3000 为3级,工资>2000 为2级,工资<=2000 为1级)
-- 语法:case when ... then ... when ... then ... else ... end
--1.列出至少有一个员工的所有部门。
SELECT dept.deptno,COUNT(*) FROM dept
LEFT OUTER JOIN emp
ON dept.`deptno`=emp.`deptno`
GROUP BY deptno
HAVING COUNT(empno)>=1;
SELECT DISTINCT(deptno),COUNT(empno) FROM emp GROUP BY deptno;
SELECT dept.`deptno`,COUNT(*) FROM dept
LEFT OUTER JOIN emp
ON dept.`deptno`=emp.`deptno`
GROUP BY deptno
HAVING COUNT(empno)>=1;
-- 2.列出薪金比"刘一"多的所有员工。
SELECT sal FROM emp WHERE ename='刘一';
SELECT * FROM emp WHERE sal>(SELECT sal FROM emp WHERE ename='刘一');
-- 3.***** 列出所有员工的姓名及其直接上级的姓名。
--自身连接
SELECT e1.ename,e2.ename FROM emp AS e1,emp AS e2
WHERE e1.mgr=e2.empno;
-- 4.列出受雇日期早于其直接上级的所有员工。
SELECT e1.ename,e1.`hiredate`,e2.ename,e2.`hiredate` FROM emp AS e1,emp AS e2
WHERE e1.mgr=e2.empno AND e1.`hiredate`<e2.`hiredate`;
-- 5.列出部门名称和这些部门的员工信息,同时列出那些没有员工的部门。
SELECT dept.`dname`,emp.* FROM dept
LEFT OUTER JOIN emp ON
dept.`deptno`=emp.`deptno`;
-- 6.列出所有job为“职员”的姓名及其部门名称。
SELECT emp.`ename`,dept.`dname` FROM emp
LEFT OUTER JOIN dept ON dept.`deptno`=emp.`deptno`
WHERE job='职员';
-- 7.列出最低薪金大于1500的各种工作。
SELECT DISTINCT job,sal,deptno FROM emp
HAVING sal>1500
ORDER BY sal ASC;
SELECT DISTINCT job,sal,deptno FROM emp WHERE sal>1500 ;
SELECT DISTINCT job,MIN(sal) FROM emp
GROUP BY job
HAVING MIN(sal)>1500;
-- 8.列出在部门 "销售部" 工作的员工的姓名,假定不知道销售部的部门编号。
SELECT deptno FROM dept WHERE dname='销售部';
SELECT dept.`dname`,emp.* FROM dept LEFT OUTER JOIN emp ON (SELECT deptno FROM dept WHERE dname='销售部')=emp.`deptno`;
SELECT dname,ename FROM dept,emp WHERE dept.`deptno`=emp.`deptno` AND dept.`dname`='销售部';
-- 9.列出薪金高于公司平均薪金的所有员工。
SELECT * FROM emp WHERE sal>(SELECT AVG(sal) FROM emp);
-- 10.列出与"周八"从事相同工作的所有员工。
SELECT ename,job FROM emp WHERE job=(SELECT job FROM emp WHERE ename='周八');
-- 11.列出薪金等于部门30中员工的薪金的所有员工的姓名和薪金。
SELECT ename,sal FROM emp WHERE deptno=30;
SELECT * FROM emp
WHERE sal IN (SELECT sal FROM emp WHERE deptno=30);
-- 12.列出薪金高于在部门30工作的所有员工的薪金的员工姓名和薪金。
SELECT * FROM emp WHERE sal > ALL(SELECT sal FROM emp WHERE deptno=30);
-- 13.列出在每个部门工作的员工数量、平均工资。
SELECT deptno,COUNT(empno),AVG(sal) FROM emp GROUP BY deptno;
SELECT dept.dname,COUNT(emp.empno),AVG(sal)
FROM dept
LEFT OUTER JOIN emp
ON dept.deptno=emp.deptno
GROUP BY dept.deptno;
-- 14.列出所有员工的姓名、部门名称和工资。
SELECT ename, dept.dname,sal
FROM emp, dept
WHERE emp.deptno=dept.deptno;
SELECT ename,dept.`dname`,sal
FROM emp
LEFT OUTER JOIN dept
ON dept.`deptno`=emp.`deptno`;
-- 15.列出所有部门的详细信息和部门人数。
SELECT deptno,COUNT(empno) FROM emp
GROUP BY deptno;
SELECT dept.*,COUNT(empno) FROM emp
LEFT OUTER JOIN dept
ON dept.`deptno`=emp.`deptno`
GROUP BY deptno;
-- 16.列出各种工作的最低工资。
SELECT job,MIN(sal)
FROM emp
GROUP BY job;
-- 17.列出各个部门的 经理 的最低薪金。
SELECT deptno,MIN(sal)
FROM emp
WHERE job='经理'
GROUP BY deptno;
-- 18.列出所有员工的年工资,按年薪从低到高排序。
SELECT ename,(sal+IFNULL(comm,0))*12 yearsal FROM emp
ORDER BY yearsal ASC;
-- 19.查出emp表中薪水在3000以上(包括3000)的所有员工的员工号、姓名、薪水。
SELECT * FROM emp WHERE sal>=3000;
-- 20.查询出所有薪水在'陈二'之上的所有人员信息。
SELECT * FROM emp WHERE sal>(SELECT sal FROM emp WHERE ename='陈二');
-- 21.查询出emp表中部门编号为20,薪水在2000以上(不包括2000)的所有员工,
-- 显示他们的员工号,姓名以及薪水,以如下列名显示:员工编号 员工名字 薪水
SELECT empno 员工编号,ename 员工名字,sal 薪水 FROM emp WHERE deptno=20 AND sal>2000;
-- 22.查询出emp表中所有的工作种类(无重复)
SELECT DISTINCT job FROM emp;
-- 23.查询出所有奖金(comm)字段不为空的人员的所有信息。
SELECT * FROM emp WHERE comm IS NOT NULL;
-- 24.查询出薪水在800到2500之间(闭区间)所有员工的信息。(注:使用两种方式实现and以及between and)
SELECT * FROM emp WHERE sal>=800 AND sal<=2500;
SELECT * FROM emp WHERE sal BETWEEN 800 AND 2500;
-- 25.查询出员工号为7521,7900,7782的所有员工的信息。(注:使用两种方式实现,or以及in)
SELECT * FROM emp WHERE empno=7521 OR empno=7900 OR empno=7782;
SELECT * FROM emp WHERE empno IN(7521,7900,7782);
-- 26.查询出名字中有“张”字符,并且薪水在1000以上(不包括1000)的所有员工信息。
SELECT * FROM emp WHERE ename LIKE '张%' AND sal>1000;
-- 27.查询出名字第三个汉字是“多”的所有员工信息。
SELECT * FROM emp WHERE ename LIKE '__多';
-- 28.将所有员工按薪水升序排序,薪水相同的按照入职时间降序排序。
SELECT * FROM emp
ORDER BY sal ASC,hiredate DESC;
-- 29.将所有员工按照名字首字母升序排序,首字母相同的按照薪水降序排序。 order by convert(name using gbk) asc;
SELECT * FROM emp
ORDER BY CONVERT(SUBSTRING(ename,1,1) USING gbk) ASC,sal DESC;
-- 30.查询出最早工作的那个人的名字、入职时间和薪水。
SELECT ename,sal,MIN(hiredate) FROM emp;
SELECT * FROM emp
WHERE hiredate = (SELECT MIN(hiredate) FROM emp);
-- 31.显示所有员工的名字、薪水、奖金,如果没有奖金,暂时显示100.
SELECT ename,sal,IFNULL(comm,100) FROM emp;
-- 32.显示出薪水最高人的职位。
SELECT job FROM emp WHERE sal=(SELECT MAX(sal) FROM emp);
-- 33.查出emp表中所有部门的最高薪水和最低薪水,部门编号为10的部门不显示。
SELECT deptno,MAX(sal),MIN(sal) FROM emp GROUP BY deptno HAVING deptno != 10;
-- 34.删除10号部门薪水最高的员工。
SELECT * FROM emp
WHERE deptno=10 AND sal>=ALL(SELECT sal FROM emp WHERE deptno=10);
DELETE FROM emp WHERE empno=(SELECT empno FROM (SELECT empno FROM emp
WHERE deptno=10
AND sal>=ALL(SELECT sal FROM emp WHERE deptno=10)) AS temp);
-- 35.将薪水最高的员工的薪水降30%。
SELECT empno FROM emp
WHERE sal >= ALL(SELECT sal FROM emp);
UPDATE emp
SET sal=sal*0.7
WHERE empno IN (SELECT temp.empno FROM (SELECT empno FROM emp
WHERE sal >= ALL(SELECT sal FROM emp)) temp);
-- 36.查询员工姓名,工资和 工资级别(工资>=3000 为3级,工资>2000 为2级,工资<=2000 为1级)
-- 语法:case when ... then ... when ... then ... else ... end
SELECT ename,
CASE WHEN sal>3000 THEN '3级'
WHEN sal>2000 THEN '2级'
ELSE '1级'
END
FROM emp;