MySql基础入门到进阶

柔情只为你懂 2022-05-12 20:08 327阅读 0赞

1. 基本操作

登录:

  1. mysql -u root -p

输入密码,登陆数据库

创建数据库 study

  1. CREATE DATABASE study CHARACTER SET UTF8;

删除数据库:

  1. DROP DATABASE 数据库名;

查看所有的数据库:

  1. SHOW DATABASES;

使用数据库 study

  1. USE study;

创建数据表 news

  1. CREATE TABLE news (
  2. nid INT AUTO_INCREMENT,
  3. title VARCHAR(30) NOT NULL,
  4. content VARCHAR(300),
  5. CONSTRAINT pk_nid PRIMARY KEY(nid)
  6. )ENGINE = INNODB DEFAULT CHARSET=utf8;

插入数据:

  1. INSERT INTO news(title, content) VALUES('mysql study', 'this is test data');
  2. INSERT INTO news(title, content) VALUES('mysql study', 'this is test data');
  3. INSERT INTO news(title, content) VALUES('mysql study', 'this is test data');

主键设置了自增,所以插入数据时可以省略此字段

获得自增后的主键:

  1. SELECT LAST_INSERT_ID();

删除数据:

  1. DELETE FROM news WHERE nid = 2;

修改数据:

  1. UPDATE news SET content='this is update test' WHERE nid=1;

2. 查询操作

数据表准备:

  1. --
  2. -- Table structure for table `dept`
  3. --
  4. DROP TABLE IF EXISTS `dept`;
  5. CREATE TABLE `dept` (
  6. `deptno` INT(2) NOT NULL,
  7. `dname` VARCHAR(14) DEFAULT NULL,
  8. `loc` VARCHAR(13) DEFAULT NULL,
  9. PRIMARY KEY (`deptno`)
  10. ) ENGINE=INNODB DEFAULT CHARSET=utf8;
  11. --
  12. -- Dumping data for table `dept`
  13. --
  14. INSERT INTO `dept` VALUES (10,'ACCOUNTING','NEW YORK'),(20,'RESEARCH','DALLAS'),(30,'SALES','CHICAGO'),(40,'OPERATIONS','BOSTON'),(60,'HR','SY');
  15. --
  16. -- Table structure for table `emp`
  17. --
  18. DROP TABLE IF EXISTS `emp`;
  19. CREATE TABLE `emp` (
  20. `empno` INT(4) NOT NULL,
  21. `ename` VARCHAR(20) DEFAULT NULL,
  22. `job` VARCHAR(9) DEFAULT NULL,
  23. `mgr` INT(4) DEFAULT NULL,
  24. `hiredate` DATE DEFAULT NULL,
  25. `sal` DOUBLE(7,2) DEFAULT NULL,
  26. `comm` DOUBLE(7,2) DEFAULT NULL,
  27. `deptno` INT(2) DEFAULT NULL,
  28. PRIMARY KEY (`empno`)
  29. ) ENGINE=INNODB DEFAULT CHARSET=utf8;
  30. --
  31. -- Dumping data for table `emp`
  32. --
  33. INSERT INTO `emp` VALUES (7369,'SMITH','CLERK',7902,'1980-12-17',800.00,NULL,20),(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600.00,300.00,30),(7521,'WARD','SALESMAN',7698,'1981-02-22',1250.00,500.00,30),(7566,'JONES','MANAGER',7839,'1981-04-02',2975.00,NULL,20),(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250.00,1400.00,30),(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850.00,NULL,30),(7782,'CLARK','MANAGER',7839,'1981-06-09',2450.00,NULL,10),(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000.00,NULL,20),(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000.00,NULL,10),(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500.00,0.00,30),(7876,'ADAMS','CLERK',7788,'1987-05-23',1100.00,NULL,20),(7900,'JAMES','CLERK',7698,'1981-12-03',950.00,NULL,30),(7902,'FORD','ANALYST',7566,'1981-12-03',3000.00,NULL,20),(7934,'MILLER','CLERK',7782,'1982-01-23',1300.00,NULL,10);
  34. --
  35. -- Table structure for table `salgrade`
  36. --
  37. DROP TABLE IF EXISTS `salgrade`;
  38. CREATE TABLE `salgrade` (
  39. `grade` INT(11) DEFAULT NULL,
  40. `losal` INT(11) DEFAULT NULL,
  41. `hisal` INT(11) DEFAULT NULL
  42. ) ENGINE=INNODB DEFAULT CHARSET=utf8;
  43. --
  44. -- Dumping data for table `salgrade`
  45. --
  46. INSERT INTO `salgrade` VALUES (1,700,1200),(2,1201,1400),(3,1401,2000),(4,2001,3000),(5,3001,9999);
  47. --
  48. -- Table structure for table `bonus`
  49. --
  50. CREATE TABLE `bonus` (
  51. `ename` VARCHAR(20) DEFAULT NULL,
  52. `job` VARCHAR(9) DEFAULT NULL,
  53. `sal` DOUBLE(7,2) DEFAULT NULL,
  54. `comm` DOUBLE(7,2) DEFAULT NULL
  55. ) ENGINE = INNODB default charset utf8;

2.1 简单查询

最基本的查询,语法:

  1. SELECT [DISTINCT] *|列[别名],列[别名],...
  2. FROM 表名称 [别名];
  • 语句执行顺序:1. FROM 2. SELECT;
  • DISTINCT :去掉重复行数据;
  • * :查询所有列,如果不想查询所有列,可以写上具体列名称;
  • 在拿到一个未知的数据库是,千万不要查询全部,不然…
  • 别名:与列名称以空格分开,将替代原列名称显示在结果的表头;
  • SELECT 子句中的“列”可以进行数学计算 + - * / %。

例一:

  1. select empno 员工编号, ename 员工姓名, job 岗位, mgr 领导, hiredate 入职日期, sal*12 年薪, comm 奖金, deptno 部门编号 from emp;

练习

要求:查询所有雇员编号,雇员姓名, 每月总收入,所在部门。

  1. select empno, ename, sal + ifnull(comm,0) from emp;
  • NULL 值和任何数据进行计算,结果都是 NULL
  • IFNULL(expr1,expr2) 如果 expr1 不是 NULLIFNULL() 返回 expr1,否则它返回 expr2

2.2 分页查询

数据库级别的分页查询,语法:

  1. 查询语句 LIMIT 开始行,长度;
  • 开始行索引从 0 开始。
  • LIMIT 子句是整个查询的最后一句.

2.3 限定查询

针对查询结果进行条件过滤,语法:

  1. SELECT [DISTINCT] *|列[别名],列[别名],...
  2. FROM 表名称 [别名]
  3. [WHERE 条件];
  • 语句执行顺序:1. FROM 2. WHERE 3. SELECT
    WHERE子句中的条件可以是多条,该子句可以以下操作符:

    • 关系运算符:> >= < <= <> !=
    • 范围运算符:BETWEEN ... AND ...

      • 字段 BETWEEN 最小值 AND 最大值
      • 注意:范围中包含最大值和最小值;
    • 逻辑运算符:AND(与)、 OR(或)、 NOT(非);
    • 空判断运算符:IS NULLIS NOT NULL

      • 不能用逻辑运算符来进行空判断,比如“comm != null”,正确的是 “comm IS NOT NULL”;
      • 注意,对数据库来说,0 和 null 是不一样的概念;
    • 基数范围:INNOT IN

      • 表示在或者不在某几个可选数值范围之中;
      • 字段 IN/NOT IN (数值, 数值,...)
      • 注意:在使用 NOT IN 时,如果基数中包含了 NULL ,则无论有没有满足要求的数据,都不会被显示,这是一种保护机制;
    • 模糊查询:LIKE

      • 字段 LIKE '匹配的关键字'
      • _:表示匹配任意 一个 字符
      • %:表示匹配任意 0个,1个,或多个关键字;
      • 如果不使用任何通配符,则表示精确匹配。

相关练习

  1. 要求:查询出所有销售人员(SALESMAN)中工资高于 1200 的雇员信息。

    SELECT * FROM emp WHERE job = ‘salesman’ AND sal > 1200;

  2. 要求:查询出工资在 1200 ~ 3000 之间的雇员信息。

    SELECT * FROM emp WHERE sal BETWEEN 1200 AND 3000;

  3. 要求:查询出所有在 1981 年雇佣的雇员信息。

    SELECT * FROM emp WHERE hiredate BETWEEN ‘1981-01-01’ AND ‘1981-12-31’;

  4. 要求:查询所有领取佣金的雇员,即 comm 不为 null。

    SELECT * FROM emp WHERE comm IS NOT NULL;

  5. 要求:查询出雇员编号为 7369,7566,7839,8899 的雇员。

    SELECT * FROM emp WHERE empno IN (7369,7566,7839,8899);

  6. 要求:查询出姓名中第二个字母是’A’的雇员。

    SELECT * FROM emp WHERE ename LIKE ‘_A%’;

2.4 查询排序

对查询的结果进行排序,语法:

  1. SELECT [DISTINCT] *|列[别名],列[别名],...
  2. FROM 表名称 [别名]
  3. [WHERE 条件]
  4. [ORDER BY 字段[ASC|DESC],字段[ASC|DESC],...];
  • 语句执行顺序:1. FROM 2. WHERE 3. SELECT 4. ORDER BY
  • 因为ORDER BY子句是整个查询的倒数第二句,所以可以使用在 SELECT 子句中定义的别名;
  • ASC 表示升序,是默认方式
  • DESC 表示降序。

相关练习

  1. 要求:查询出所有雇员的信息,结果按照工资由高到低排序,如果工资相同,则按照入职日期从早到晚排序。

    SELECT * FROM emp ORDER BY sal DESC, hiredate;

  2. 要求:查询出所有雇员的信息,按照年薪排序。

    SELECT FROM emp ORDER BY sal12 ;

2.5 思考题

  1. 要求:查询部门 30 中的所有员工。

    select * from emp where deptno = 30;

  2. 要求:查询所有办事员(CLERK)的姓名,编号,部门编号。

    select ename, empno,job, deptno from emp where job = ‘CLERK’;

  3. 要求:查询出佣金高于工资 60 % 的雇员。

    select from emp where comm > sal0.6

  4. 要求:查询出部门 10 中所有的经理 和 部门 20 中所有的办事员。

    select * from emp where (deptno = 10 and job = ‘manager’) or (deptno = 20 and job = ‘CLERK’);

  5. 要求:查询出部门 10 中所有的经理 、 部门 20 中所有的办事员 、 既不是经理又不是办事员,但其工资大于等于 2000 的雇员信息。

    select * from emp where (deptno = 10 and job = ‘manager’) or (deptno = 20 and job = ‘CLERK’) or (job != ‘manager’ and job != ‘clerk’ and sal > 2000);

  6. 要求:查询出收入组成中有佣金的员工的不同工作。

    select distinct job from emp where comm is not null;

  7. 要求:查询出收入组成中无佣金 或者佣金收入低于 100 的雇员信息。

    select * from emp where comm is null or comm < 100;

  8. 要求:查询姓名中没有 “R” 的员工姓名。

    select ename from emp where ename not like ‘%R%’;

  9. 要求:查询姓名中有 “A” 的雇员信息,结果按照工资由高到低排序,如果工资相同,则按照入职早晚排序,如果入职日期相同,则按照岗位排序。

    select * from emp where ename like ‘%A%’ order by sal desc,hiredate,job;

2.6 多表查询

具有关联字段的表,以关联字段关系判断进行的查询,其中内连接查询语法:

  1. SELECT [DISTINCT] *|列[别名],列[别名],...
  2. FROM 数据表1 [别名], 数据表2 [别名],...
  3. WHERE 数据表1.关联字段 = 数据表2.关联字段...
  4. [ORDER BY 字段[ASC|DESC],字段[ASC|DESC],...];
  5. 或者
  6. SELECT [DISTINCT] *|列[别名],列[别名],...
  7. FROM 数据表1 [别名] [INNER] JOIN 数据表2 [别名] ON 关联条件
  8. [ORDER BY 字段[ASC|DESC],字段[ASC|DESC],...];
  • 如果指定关联表,但是并不在 WHERE 子句中指定关联字段,则查询的结果将会是两个数据表的”笛卡儿积”,其实指定了关联字段,这个”笛卡儿积”依然存在,只是被过滤掉了。所以,在数据量比较大的情况下,多表关联查询的效率是比较差的。

相关练习

  1. 要求:查询每个雇员的编号,姓名,职位,工资,部门名称,部门位置。

    SELECT empno, ename, job, sal, dname, loc FROM emp, dept WHERE emp.deptno = dept.deptno;

    — 或者

    SELECT empno, ename, job, sal, dname, loc FROM emp INNER JOIN dept ON dept.deptno=emp.deptno;

  2. 要求:查询每个雇员的编号,姓名,职位,工资,入职日期,工资等级。

    SELECT empno, ename, job, sal, hiredate, grade FROM emp, salgrade WHERE emp.sal BETWEEN salgrade.losal AND salgrade.hisal;

    — 或者

    SELECT empno, ename, job, sal, hiredate, grade FROM emp INNER JOIN salgrade ON emp.sal BETWEEN salgrade.losal AND salgrade.hisal;

多表查询的连接方式

多表查询的连接方式有两种:

  • 内连接:默认为等值连接、上面的案例就是内连接;

在使用内连接进行多表查询的时候,如果其中一条数据,在关联表中没有符合条件的对应值,这时本条数据就不会被显示。

案例:查询每个雇员的编号,姓名,职位,领导姓名.
使用内连接:

  1. SELECT e.empno, e.ename, e.job, m.ename FROM emp e, emp m WHERE e.mgr = m.empno;
  2. -- 或者
  3. SELECT e.empno, e.ename, e.job, m.ename FROM emp e inner join emp m on e.mgr = m.empno;

可以看到,编号 7839 的雇员信息没有出现在结果中,因为他没有领导(老大).

  • 外连接:分为 左外连接、右外连接、全连接。
    外连接语法:

    SELECT [DISTINCT] *|列[别名],列[别名],…
    FROM 数据表1 [别名] LEFT|RIGHT|FULL [OUTER] JOIN 数据表2 [别名] ON 关联条件
    [ORDER BY 字段[ASC|DESC],字段[ASC|DESC],…];

注意:全外连接的 FULL,MySQL 暂不支持,需要使用 查询结果连接符 UNIONUNION 操作会将两个 SELECT 语句的查询结果连接起来,并且会去掉重复数据。

用外连接改写上面的案例:

  1. SELECT e.empno, e.ename, e.job, m.ename FROM emp e left outer join emp m on e.mgr = m.empno;

为了更清楚的说明问题,新建一张 myemp 的表,减少 emp 表中的字段:

  1. CREATE TABLE myemp
  2. ( `empno` INT(4) NOT NULL AUTO_INCREMENT,
  3. `ename` VARCHAR(20) DEFAULT NULL,
  4. `deptno` INT(2) DEFAULT NULL,
  5. CONSTRAINT pk_empno PRIMARY KEY (`empno`)
  6. )ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

插入测试数据:

  1. INSERT INTO myemp(ename, deptno) VALUES('SMITH', 10);
  2. INSERT INTO myemp(ename, deptno) VALUES('ALLEN', 10);
  3. INSERT INTO myemp(ename, deptno) VALUES('WARD', 10);
  4. INSERT INTO myemp(ename, deptno) VALUES('JONES', 20);
  5. INSERT INTO myemp(ename, deptno) VALUES('MARTIN', 20);
  6. INTO myemp(ename, deptno) VALUES('BLAKE', 30);
  7. INSERT INTO myemp(ename, deptno) VALUES('CLARK', 40);
  8. INSERT INTO myemp(ename, deptno) VALUES('SCOTT', 50);

先观察内连接(等值连接):

  1. SELECT * FROM myemp m INNER JOIN dept d ON m.`deptno`=d.`deptno`;

查询结果:

  1. mysql> SELECT * FROM myemp e INNER JOIN dept d ON e.`deptno`=d.`deptno`;
  2. +-------+--------+--------+--------+------------+----------+
  3. | empno | ename | deptno | deptno | dname | loc |
  4. +-------+--------+--------+--------+------------+----------+
  5. | 1 | SMITH | 10 | 10 | ACCOUNTING | NEW YORK |
  6. | 2 | ALLEN | 10 | 10 | ACCOUNTING | NEW YORK |
  7. | 3 | WARD | 10 | 10 | ACCOUNTING | NEW YORK |
  8. | 4 | JONES | 20 | 20 | RESEARCH | DALLAS |
  9. | 5 | MARTIN | 20 | 20 | RESEARCH | DALLAS |
  10. | 6 | BLAKE | 30 | 30 | SALES | CHICAGO |
  11. | 7 | CLARK | 40 | 40 | OPERATIONS | BOSTON |
  12. +-------+--------+--------+--------+------------+----------+
  13. 7 rows in set (0.00 sec)

可以发现,结果是两个表关联字段的交集:
70

左外连接:

  1. SELECT * FROM myemp m LEFT OUTER JOIN dept d ON m.`deptno`=d.`deptno`;

查询结果:

  1. mysql> SELECT * FROM myemp m LEFT OUTER JOIN dept d ON m.`deptno`=d.`deptno`;
  2. +-------+--------+--------+--------+------------+----------+
  3. | empno | ename | deptno | deptno | dname | loc |
  4. +-------+--------+--------+--------+------------+----------+
  5. | 1 | SMITH | 10 | 10 | ACCOUNTING | NEW YORK |
  6. | 2 | ALLEN | 10 | 10 | ACCOUNTING | NEW YORK |
  7. | 3 | WARD | 10 | 10 | ACCOUNTING | NEW YORK |
  8. | 4 | JONES | 20 | 20 | RESEARCH | DALLAS |
  9. | 5 | MARTIN | 20 | 20 | RESEARCH | DALLAS |
  10. | 6 | BLAKE | 30 | 30 | SALES | CHICAGO |
  11. | 7 | CLARK | 40 | 40 | OPERATIONS | BOSTON |
  12. | 8 | SCOTT | 50 | NULL | NULL | NULL |
  13. +-------+--------+--------+--------+------------+----------+
  14. 8 rows in set (0.00 sec)

可以发现,结果是左边的表是完整的,右边的表只保留交集部分:

70 1
右外连接:

  1. SELECT * FROM myemp m RIGHT OUTER JOIN dept d ON m.`deptno`=d.`deptno`;

查询结果:

  1. mysql> SELECT * FROM myemp m RIGHT OUTER JOIN dept d ON m.`deptno`=d.`deptno`;
  2. +-------+--------+--------+--------+------------+----------+
  3. | empno | ename | deptno | deptno | dname | loc |
  4. +-------+--------+--------+--------+------------+----------+
  5. | 1 | SMITH | 10 | 10 | ACCOUNTING | NEW YORK |
  6. | 2 | ALLEN | 10 | 10 | ACCOUNTING | NEW YORK |
  7. | 3 | WARD | 10 | 10 | ACCOUNTING | NEW YORK |
  8. | 4 | JONES | 20 | 20 | RESEARCH | DALLAS |
  9. | 5 | MARTIN | 20 | 20 | RESEARCH | DALLAS |
  10. | 6 | BLAKE | 30 | 30 | SALES | CHICAGO |
  11. | 7 | CLARK | 40 | 40 | OPERATIONS | BOSTON |
  12. | NULL | NULL | NULL | 60 | HR | SY |
  13. +-------+--------+--------+--------+------------+----------+
  14. 8 rows in set (0.00 sec)

可以发现,结果是右边的表是完整的,左边的表只保留交集部分:
70 2

全外连接:

  1. SELECT * FROM myemp m LEFT OUTER JOIN dept d ON m.`deptno`=d.`deptno`
  2. UNION
  3. SELECT * FROM myemp m RIGHT OUTER JOIN dept d ON m.`deptno`=d.`deptno`;

查询结果:

  1. mysql> SELECT * FROM myemp m LEFT OUTER JOIN dept d ON m.`deptno`=d.`deptno`
  2. -> UNION
  3. -> SELECT * FROM myemp m RIGHT OUTER JOIN dept d ON m.`deptno`=d.`deptno`;
  4. +-------+--------+--------+--------+------------+----------+
  5. | empno | ename | deptno | deptno | dname | loc |
  6. +-------+--------+--------+--------+------------+----------+
  7. | 1 | SMITH | 10 | 10 | ACCOUNTING | NEW YORK |
  8. | 2 | ALLEN | 10 | 10 | ACCOUNTING | NEW YORK |
  9. | 3 | WARD | 10 | 10 | ACCOUNTING | NEW YORK |
  10. | 4 | JONES | 20 | 20 | RESEARCH | DALLAS |
  11. | 5 | MARTIN | 20 | 20 | RESEARCH | DALLAS |
  12. | 6 | BLAKE | 30 | 30 | SALES | CHICAGO |
  13. | 7 | CLARK | 40 | 40 | OPERATIONS | BOSTON |
  14. | 8 | SCOTT | 50 | NULL | NULL | NULL |
  15. | NULL | NULL | NULL | 60 | HR | SY |
  16. +-------+--------+--------+--------+------------+----------+
  17. 9 rows in set (0.00 sec)

可以发现,全外连接中,两个表都保证了完整性:

70 3

总结:
如果想要保证 JOIN 左边的表的完整性,使用 LEFT OUTER JOIN 连接;
如果想要保证 JOIN 右边的表的完整性,使用 RIGHT OUTER JOIN 连接;
如果想要保证 JOIN 两边的表的完整性,使用 UNION 连接左外连接查询和右外连接查询;
如果对表的完整性没有要求,或者两张表的关联字段取值相等,则使用 INNER JOIN

三个表的关联查询

案例:要求:查询雇员编号,雇员姓名,雇员职位,所在部门名称,工资等级。

  1. SELECT e.`empno`, e.`ename`, e.`job`, d.`dname`, s.grade
  2. FROM (emp e LEFT OUTER JOIN dept d ON e.`deptno` = d.`deptno`)
  3. LEFT OUTER JOIN salgrade s ON e.`sal` BETWEEN s.`losal` AND s.hisal;

2.7 分组统计查询

常见的统计函数

  • COUNT() : 计数,统计记录数

    SELECT COUNT(*) FROM emp;

  • SUM() : 求和

    SELECT SUM(sal) 总工资 FROM emp;

  • AVG() : 求平均值

    SELECT AVG(sal) 平均工资 FROM emp;

  • MAX() : 求最大值

    SELECT MAX(sal) 最高工资 FROM emp;

  • MIN() : 求最小值

    SELECT MIN(sal) 最高工资 FROM emp;

注意,COUNT(*)COUNT(字段)COUNT(DISTINCT 字段)的区别?

  • COUNT(*):统计除表中实际的数据量;
  • COUNT(字段):不会统计 NULL 值字段;
  • COUNT(DISTINCT 字段):消除重复数据后的统计结果。

分组统计

需要注意的是,有相同特征的事物才可以进行分组,对于数据库来说,有重复数据才可以进行分组。
分组统计查询的语法如下:

  1. SELECT [DISTINCT] *|列[别名],列[别名],... | 统计函数
  2. FROM 数据表 [别名], 数据表 [别名],...
  3. [WHERE 条件]
  4. [GROUP BY 分组字段,分组字段,...]
  5. [HAVING 分组后过滤条件]
  6. [ORDER BY 字段 [ASC|DESC],字段 [ASC|DESC],...]
  7. [LIMIT 开始行, 显示行数]
  • 语句执行顺序:1. FROM 2. WHERE 3. GROUP BY 4. HAVING 5. SELECT 6.ORDER BY 7. LIMIT
  • 统计查询在Oracle数据库中的限制:

    • 统计函数单独使用时(没有GROUP BY 子句),查询语句中只能够出现统计函数,不能够出现其他字段;
    • 使用分组统计查询时(有GROUP BY 子句),SELECT 子句中只能够出现统计函数和分组字段,其他任何字段都不允许出现;

相关练习

  1. 要求:查询出每个部门的名称,部门人数,平均工资。

    SELECT d.dname, COUNT(e.empno) 部门人数, AVG(e.sal) 平均工资
    FROM emp e RIGHT OUTER JOIN dept d ON e.deptno = d.deptno
    GROUP BY d.dname;

  2. 要求:查询出每个部门的编号,名称,位置,部门人数,平均工资。

    SELECT d.deptno, d.dname, d.loc, COUNT(e.empno) 部门人数, AVG(e.sal) 平均工资
    FROM emp e RIGHT OUTER JOIN dept d ON e.deptno = d.deptno
    GROUP BY d.deptno, d.dname, d.loc;

  3. 要求:按照职位分组,统计每个职位的平均工资,要求显示的是平均工资高于2000的职位信息。

    SELECT job, AVG(sal)
    FROM emp
    GROUP BY job
    HAVING AVG(sal) > 2000;

注意 WHEREHAVING 的区别:
WHERE 子句在分组之前执行,所以数据先经过 WHERE 子句筛选后才进行的分组,且 WHERE 子句不能使用统计函数。
HAVING 子句必须结合 GROUP BY 子句一起出现,是分组后的过滤,可以使用统计函数。

  1. 要求:统计公司每个工资等级的人数,平均工资。

    SELECT s.grade, COUNT(e.empno) 人数, AVG(e.sal) 平均工资
    FROM emp e INNER JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal
    GROUP BY s.grade;

2.8 子查询

子查询是复杂查询中最为重要的。所谓子查询,就是在一条查询语句中嵌套若干条查询。子查询可以改善关联查询的性能。

子查询可以嵌套在 SELECTFROMWHEREHAVING 子句中,嵌套的语句用 () 包裹起来。

根据子查询语句返回的结果,其应该出现的位置及用法如下:

  • 子查询返回的结果是 单行单列,也就是说,返回的结果就是一个数据,那么它通常用于条件判断,所以经常出现在 WHEREHAVING 子句中,这是直接使用它做条件判断即可;
  • 子查询返回的结果是 多行单列,也就是说,返回的结果是一组数据,那么它通常出现在 WHERE 子句中,需要配合如下符号使用:

    • IN : 在指定的范围内,可以配合 NOT 使用,表示不在范围内。需要注意的是, NOT IN 的范围中不能包含 NULL 值;
    • ANY : 指“任何”的意思,配合 >=< 使用;

      • =ANY : 功能和 IN 一样

        SELECT * FROM emp
        WHERE sal = ANY(SELECT sal FROM emp WHERE job = ‘manager’)

      • >ANY : 比子查询结果中的最小值大

        SELECT * FROM emp
        WHERE sal >ANY(SELECT sal FROM emp WHERE job = ‘manager’)

      • <ANY : 比子查询结果中的最大值小

        SELECT * FROM emp
        WHERE sal >ANY(SELECT sal FROM emp WHERE job = ‘manager’)

    • ALL : 指“所有”的意思,同样配合 >=< 使用;

      • >ALL : 比子查询结果中的最大值大
      • <ALL : 比子查询结果中的最小值小
  • 子查询的结果返回多行多列,即,返回的结果是一个数据表,那么它通常出现在 FROM 子句中,作为一个虚拟表使用,最好配合别名使用。

相关练习

  1. 要求:查询最早入职的雇员信息。

    SELECT *
    FROM emp
    WHERE hiredate = (

    1. SELECT MIN(hiredate)
    2. FROM emp);
  2. 要求:查询与 Scott 从事同一工作,且工资相同的雇员信息。

    SELECT *
    FROM emp
    WHERE (job, sal) = (

    1. SELECT job, sal
    2. FROM emp
    3. WHERE ename = 'scott') AND ename <> 'scott';
  3. 要求:查询出平均工资高于公司平均工资的职位名称,职位人数,平均工资。

    SELECT job, COUNT(empno), AVG(sal)
    FROM emp
    GROUP BY job
    HAVING AVG(sal) > (SELECT AVG(sal) FROM emp);

2.9 复杂查询综合练习

  1. 要求:列出至少有一个员工的部门信息。

    SELECT d.deptno, d.dname, d.loc, COUNT(e.empno)
    FROM dept d LEFT OUTER JOIN emp e ON d.deptno = e.deptno
    GROUP BY d.deptno
    HAVING COUNT(e.empno) > 0;

  2. 要求:查询出入职日期早于直接上级的所有员工的编号,姓名,部门名称,上级姓名。

    SELECT e.empno, e.ename,e.hiredate, d.dname, m.ename, m.hiredate
    FROM (emp e LEFT OUTER JOIN dept d ON e.deptno = d.deptno)

    1. LEFT OUTER JOIN emp m ON e.`mgr` = m.`empno`

    WHERE e.hiredate < m.hiredate;

  3. 要求:查询出工资比 SMITH 高的所有员工信息。

    SELECT *
    FROM emp
    WHERE sal > (SELECT sal FROM emp WHERE ename = ‘smith’);

  4. 要求:查询出所有办事员(CLERK)的姓名及其所在部门名称,部门人数。

    SELECT e.ename, temp.tname, temp.count
    FROM emp e LEFT OUTER JOIN (SELECT d.deptno tno, d.dname tname, COUNT(e.empno) COUNT

    1. FROM dept d LEFT OUTER JOIN emp e ON d.`deptno` = e.`deptno`
    2. GROUP BY d.`deptno`) temp ON e.`deptno` = temp.tno

    WHERE e.job = ‘CLERK’;

  5. 要求:查询出工资大于 1500 的各种工作,以及从事此工作的雇员人数。

    SELECT job, COUNT(empno)
    FROM emp
    GROUP BY job
    HAVING MIN(sal) > 1500;

  6. 要求:查询出在 SALES 部门工作的员工姓名,工资。

    SELECT e.ename, e.sal
    FROM emp e LEFT OUTER JOIN dept d ON e.deptno = d.deptno
    WHERE d.dname = ‘SALES’;

  7. 要求:查询出工资高于公司平均工资的所有雇员姓名,所在部门,上级领导,工资等级,与具备此工资等级的雇员人数。
    分析步骤,首先查询出工资高于公司平均工资的所有雇员姓名,所在部门,上级领导,工资等级

    SELECT e.ename, d.dname, m.ename, s.grade
    FROM (emp e LEFT OUTER JOIN dept d ON e.deptno=d.deptno)

    1. INNER JOIN salgrade s ON e.`sal` BETWEEN s.`losal` AND s.`hisal`
    2. LEFT OUTER JOIN emp m ON e.`mgr` = m.`empno`

    WHERE e.sal > (SELECT AVG(sal) FROM emp);

然后查询出每个工资等级的人数:

  1. SELECT s.`grade`, COUNT(e.`empno`)
  2. FROM emp e INNER JOIN salgrade s ON e.`sal` BETWEEN s.`losal` AND s.`hisal`
  3. GROUP BY s.`grade`;

最后在第一个查询中再关联第二个查询的结果:

  1. SELECT e.`ename`, d.`dname`, m.`ename`, s.`grade`, temp.count
  2. FROM (emp e LEFT OUTER JOIN dept d ON e.`deptno`=d.`deptno`)
  3. INNER JOIN salgrade s ON e.`sal` BETWEEN s.`losal` AND s.`hisal`
  4. LEFT OUTER JOIN emp m ON e.`mgr` = m.`empno`
  5. INNER JOIN (SELECT s.`grade` tgrade, COUNT(e.`empno`) COUNT
  6. FROM emp e INNER JOIN salgrade s ON e.`sal` BETWEEN s.`losal` AND s.`hisal`
  7. GROUP BY s.`grade`) temp ON s.`grade` = temp.tgrade
  8. WHERE e.`sal` > (SELECT AVG(sal) FROM emp);
  1. 要求:查询出各种工作的最低工资及从事此工作的雇员姓名。

    SELECT temp.min, e.ename
    FROM emp e INNER JOIN (SELECT job, MIN(sal) MIN

    1. FROM emp
    2. GROUP BY job) temp

    WHERE e.job = temp.job AND e.sal = temp.min;

  2. 要求:查询出各个部门办事员(CLERK)的最低工资。

    SELECT d.dname, MIN(e.sal)
    FROM emp e LEFT OUTER JOIN dept d ON e.deptno=d.deptno
    WHERE e.job = ‘clerk’
    GROUP BY d.deptno;

  3. 要求:查询出部门名称中有 s 的部门的雇员工资合计,及部门人数。

    SELECT d.dname,SUM(e.sal), COUNT(e.empno)
    FROM dept d LEFT OUTER JOIN emp e ON d.deptno=e.deptno
    WHERE d.dname LIKE ‘%s%’
    GROUP BY d.dname;

发表评论

表情:
评论列表 (有 0 条评论,327人围观)

还没有评论,来说两句吧...

相关阅读