1、数据库三范式
- 第一范式:对关系模型的基本要求,如果不满足第一范式的数据库就不是关系型数据库,是指数据库的每一张数据库表都是不可分割的基本数据项,同一列中不能有多个值。
- 解释
- 重新设计使其满足第一范式
- 解释
- 第二范式:要求表中的每一行都能够被唯一区分。(主键)
- 遵循第二范式,如果查询时基于主键查询的,查询的性能就会高,(因为主键会自动建立索引)
- 并不是所有的表都会有根据主键查询的需求,例如上图中的user_hobby表,所以并不是所有表都要遵循第二范式。
- 第三范式:一个表不能包含其他表的其他非主键信息。
- 解释
- 使用规范
- 部门名称经常修改的时候,必须遵守第三范式,否则如果部门名称发生改变,员工表也要进行一次修改
- 部门不经常修改的时候,就不需要遵守第三范式,具体要求看实际需求。
- 解释
2、SQL分类(了解)
1、数据查询语句(DQL)
用以从表中获得数据,确定数据怎样在应用程序给出。保留字 SELECT 是 DQL(也是所有 SQL)用得最多的动词,其他 DQL 常用的保留字有 WHERE,ORDER BY,GROUP BY 和 HAVING。这些 DQL 保留字常与其他类型的 SQL 语句一起使用。
2、数据定义语言(DDL)
其语句包括动词 CREATE 和 DROP。在数据库中创建新表或删除表(CREAT TABLE 或 DROP TABLE);为表加入索引等。DDL 包括许多与人数据库目录中获得数据有关的保留字。它也是动作查询的一部分。
3、数据操作语言(DML)
其语句包括动词 INSERT,UPDATE 和 DELETE。它们分别用于添加,修改和删除表中的行,Insert / Update / Delete。也称为动作查询语言。
4、事务处理语言(TCL)
它的语句能确保被 DML 语句影响的表的所有行及时得以更新。TCL 语句包括 BEGIN TRANSACTION,COMMIT 和 ROLLBACK。
5、数据库控制语言(DCL)
它的语句通过 GRANT 或 REVOKE 获得许可,确定单个用户和用户组对数据库对象的访问。某些 RDBMS 可用 GRANT 或 REVOKE 控制对表单个列的访问。
6、指针控制语言(CCL)
它的语句,像 DECLARE CURSOR,FETCH INTO 和 UPDATE WHERE CURRENT 用于对一个或多个表单独行的操作。
3、单表查询(熟练掌握)
1、语法
SELECT [DISTINCT] * | 字段 [别名] [, 字段 [别名]]
FROM 表名称 [别名]
[WHERE 条件(S)]
[ORDER BY 字段 [ASC|DESC] [, 字段 [ASC|DESC], …]];
2、全列和投影查询
- 查询所有员工信息
SELECT * FROM employee;
- 查询每个员工的编号、姓名、职位
SELECT EMPNO,ENAME,JOB FROM employee;
- 查询所有部门信息
SELECT * FROM dept;
3、消除重复
- 查询所有有员工的部门编号
SELECT DISTINCT DEPTNO FROM emp;
- 查询有员工的部门和职位
SELECT DISTINCT DEPTNO,JOB FROM emp;
4、算数运算符
- 查询所有员工的年薪
SELECT *,(sal+IFNULL(comm,0))*12 FROM emp
- 查询所有员工的年薪(使用别名)
SELECT *,(sal+IFNULL(comm,0))*12 AS 年薪 FROM emp
5、过滤查询
1、注意事项
- 字符串和日期要使用单引号括起来
- 数字类型直接书写
- 字符串是大小写不敏感的,日期格式化大小写敏感的;
2、空值
- 空值是指不可用、未分配的值、也就是没有值
- 空值不等于0或空格,也不表示空字符串
- 任意类型都支持空值。
- 包括空值的任意运算表达式的结果都是空值
- 使用函数IFNULL(expr1,expre2),如果expr1不是null,返回expr1,否则返回expr2
3、常用算术比较运算符
-
=,!=,<>,>,>=,<,<=
-
BETWEEN … AND …:在两值之间 (包含开始和结尾,数学中的闭区间)
-
IN:匹配列出的值;如:IN(1, 2, 3, 4)
-
LIKE :匹配字符串模式, _ 、%,LIKE 运算符必须使用通配符才有意义;
- 匹配单个字符:_ 表示 1 个;
- 匹配任意多个字符:% 表示 0个、1个、多个。
- 匹配单个字符:_ 表示 1 个;
-
IS NULL:是否为空
-
查询所有员工的年薪((月薪 + 奖金) * 12)
SELECT *,(SAL+IFNULL(COMM,0)) FROM emp
- 查询有奖金的员工信息
SELECT * FROM emp WHERE COMM IS NOT NULL
- 查询公司的老板
SELECT * FROM emp WHERE MGR IS NULL
- 查询出基本工资高于 1500 的所有员工信息
SELECT * FROM emp WHERE SAL>1500
- 查询名字叫 SCOTT 的员工所从事的工作
SELECT JOB FROM emp WHERE ENAME ='SCOTT'
- 查询 1981 年入职的员工信息
SELECT * FROM emp WHERE HIREDATE BETWEEN'1981-1-1' AND '1981-12-31'
- 查询年薪小于 3W 的员工
SELECT *,(SAL+IFNULL(COMM,0)) AS yearSal FROM emp WHERE (SAL+IFNULL(COMM,0))<30000
- 查询所有不是销售人员的员工信息
SELECT * FROM emp WHERE JOB!= 'SALESMAN'
- 查询工资在 2000-3000 之间的员工信息
SELECT * FROM emp WHERE SAL BETWEEN 2000 AND 3000
- 查询工资为 800 或 1600 或 3000 的员工
SELECT * FROM emp WHERE SAL IN (800,1600,3000)
- 查询出所有雇员姓名是以 A 开头的全部雇员信息。
SELECT * FROM emp WHERE ENAME LIKE 'A%'
- 查询出雇员姓名第二个字母是 M 的全部雇员信息。
SELECT * FROM emp WHERE ENAME LIKE '_M%'
- 查询出雇员姓名任意位置上包含字母 A 的全部雇员信息。
SELECT * FROM emp WHERE ENAME LIKE '%A%'
4、逻辑运算符
-
AND:如果组合的条件都是 true,返回 true;
-
OR:如果组合的条件 之一是 true ,返回 true;
-
NOT:如果下面的条件是 false,返回 true。
-
优先级规则:比较运算符 > NOT > AND > OR
-
查询姓名中有 e 或者 a 的员工姓名
SELECT * FROM emp WHERE ENAME LIKE '%e%' OR '%a%'
- 查询工资在 1500~3000 之间的全部员工信息
SELECT * FROM emp WHERE SAL>=1500 AND SAL<=3000
- 查询工资不在 2000-3000 之间的员工信息
SELECT * FROM emp WHERE SAL<2000 OR SAL>3000
- 查询工资不为 800 或 1600 或 3000 的员工
SELECT * FROM emp WHERE SAL NOT IN (800,1600,3000)
- 查询出职位是办事员 (CLERK) 或者是销售人员 (SALESMAN) 的全部信息,且工资在 1000 以上
SELECT * FROM emp WHERE (JOB='CLERK' OR JOB='SALESMAN') AND SAL > 1000
5、结果排序
-
ASC:升序,缺省
-
DESC:降序
-
ORDER BY 子句出现在 SELECT 语句后执行;
-
ORDER BY 可以使用别名,但不能使用加了引号的别名或列名来排序,没有效果。
-
查询所有员工信息,按照工资排序
SELECT * FROM emp ORDER BY SAL DESC
- 查询所有员工信息,按照年薪降序排序
SELECT *,(SAL+IFNULL(COMM,0)) AS yearSal FROM emp ORDER BY yearSal DESC
- 查询所有员工信息,按照部门和年薪降序排序
SELECT *,(SAL+IFNULL(COMM,0)) AS yearSal FROM emp ORDER BY yearSal ASC, DEPTNO ASC
4、多表查询(熟练掌握)
1、笛卡尔积
【数学定义】: 假设集合 A = {a, b},集合 B = {0, 1, 2},则两个集合的笛卡尔积为 {(a, 0), (a, 1), (a, 2), (b, 0), (b, 1), (b, 2)}。
【MySQL 定义】: 多表查询会产生笛卡尔积,比如:SELECT * FROM emp, dept,实际运行环境下,应避免使用全笛卡尔集。
2、多表查询分类
- 内连接查询
- 隐式内连接查询
- 显示内连接查询
- 外连接查询
- 左外连接查询
- 右外连接查询
- 全外连接查询
3、内连接查询
1、隐式内连接查询
【语法】:
SELECT [DISTINCT] * | 字段 [别名] [, 字段 [别名], …]
FROM 表名称 [别名], [表名称 [别名], …]
[WHERE 条件(S)/消除笛卡尔积连接]
[ORDER BY 排序字段 [ASC|DESC] [, 排序字段 [ASC|DESC], …]];
【注意事项】:
-
在where子句中写入连接条件
-
当多个表中有重列名的时候,必须在列的名字前面加上表名作为前缀或者使用表的别名
-
等值连接时连接操作通常是在存在主外键约束条件的多表上建立的,连接条件中的两个字段通过等号建立等值关系。
-
查询员工编号,员工名称,员工所属部门的编号和名称
SELECT * FROM emp AS e,dept AS d WHERE e.DEPTNO=D.DEPTNO
- 查询员工的姓名,工资,所在部门的名称,以及工资的等级
SELECT ENAME,SAL,GRADE FROM emp AS e,dept AS d,salgrade AS s WHERE e.DEPTNO=d.DEPTNO AND e.SAL BETWEEN s.LOSAL AND s.HISAL
2、显示内连接查询
【语法】:
SELECT table1.column, table2.column
FROM table1 [INNER] JOIN table2 ON table1.column1 = table2.column2
WHERE 条件
【举例】:
- 查询员工编号,员工名称,员工所属部门的编号和名称
SELECT * FROM emp AS e
JOIN dept AS d ON e.DEPTNO=D.DEPTNO
- 查询员工的姓名,工资,所在部门的名称,以及工资的等级
SELECT ENAME,SAL,GRADE FROM emp AS e
JOIN dept AS d ON e.DEPTNO=d.DEPTNO
JOIN salgrade AS s ON e.SAL BETWEEN s.LOSAL AND s.HISAL
4、外连接查询
【需求】:
- 查询出员工的编号,名字,薪水和所在部门的名称
SELECT emp.EMPNO, emp.ENAME, emp.SAL, dept.DNAME FROM emp JOIN dept ON emp.DEPTNO = dept.DEPTNO
【问题】:
没有部门的员工则查询不出来
1、左外连接查询
SELECT emp.EMPNO, emp.ENAME, emp.SAL, dept.DNAME FROM emp LEFT JOIN dept ON emp.DEPTNO = dept.DEPTNO
【特点】:
- 查询出JOIN左边表的全部数据,右边表中不匹配的数据使用NULL填充
2、右外连接查询
SELECT emp.EMPNO, emp.ENAME, emp.SAL, dept.DNAME FROM emp RIGHT JOIN dept ON emp.DEPTNO = dept.DEPTNO
【特点】:
- 查询出JOIN右边表的全部数据,左边不匹配的数据使用NULL填充
5、分组函数(熟练掌握)
1、函数分类
- 单行函数:将每条数据进行独立的计算,然后每一条数据得到一条结果。
- 多行函数:多条数据同时计算,最终得到一条结果数据。也称为聚集函数、分组函数、主要用来完成一些统计的功能。
2、多行函数
- COUNT():查询表中的数据记录;
- AVG():求出平均值;
- SUM():求和;
- MAX():求出最大值;
- MIN():求出最小值。
【使用注意事项】:
- 统计函数忽略空值,可以使用IFNULL,因为NULL不会影响汇总值,但是会影响汇总总数量。
- 不可以在WHERE中使用分组函数。
- 在汇总总数的时候,出现在COUNT中的列不能包含空值,一般推荐使用主键或者*
【举例】:
- 查询所有员工每个月的平均工资及总工资
SELECT SUM(SAL), SUM(SAL)/COUNT(*) FROM emp
- 查询月薪在 2000 以上的员工总人数
SELECT COUNT(*) FROM emp WHERE SAL>2000
- 查询员工最高工资和最低工资差距
SELECT MAX(SAL)-MIN(SAL) FROM emp
6、分组查询
1、语法
SELECT [DISTINCT] *|分组字段1 [别名] [, 分组字段2 [别名] ,…] | 统计函数
FROM 表名称 [别名], [表名称 [别名] , …]
[WHERE 条件(s)]
[GROUP BY 分组字段1 [, 分组字段2 ,…]]
[ORDER BY 排序字段 ASC | DESC [, 排序字段 ASC | DESC]];
【举例】:
- 按照职位分组,求出每个职位的最高和最低工资
SELECT JOB,MIN(SAL),MAX(SAL) FROM emp GROUP BY JOB
- 查询出每一个部门员工的平均奖金
SELECT *,SUM(COMM)/COUNT(*) FROM emp GROUP BY DEPTNO
2、使用注意事项
- SELECT 子句出现的字段,要不在统计函数中,要不出现在 GROUP BY 子句中,否则不合理(整体与个体);
- 在GROUP BY 子句中出现的字段,可以不出现在 SELECT 列表中;
- 统计函数可以单独使用,SQL 中可以没有 GROUP BY 子句;
- 在 GROUP BY 子句中,可以按单列进行分组,也可以在多列上进行分组,多列分组就是按照多个字段的组合进行分组,最终的结果也会按照分组字段进行排序显示。
【举例】:
- 查询出每一个部门员工的平均工资
SELECT *,AVG(SAL) FROM emp GROUP BY DEPTNO
- 查询各个部门和岗位的平均工资
SELECT *,AVG(SAL) FROM emp GROUP BY DEPTNO,JOB
3、分组限定
- 不能在 WHERE 子句中对分组限定,限制组须使用 HAVING 子句(分组之后过滤需要使用 HAVING 子句);
- 不能在 WHERE 子句中使用统计函数,而在 HAVING 子句可使用统计函数。
【举例】:
- 查询部门平均工资高于 2000 的部门及其平均工资
SELECT *, AVG(SAL) FROM emp GROUP BY DEPTNO HAVING AVG(SAL)>2000
- 查询在 80, 81, 82 年各进公司多少人
SELECT COUNT(*) ,YEAR(HIREDATE) AS y FROM emp GROUP BY YEAR(HIREDATE) HAVING y BETWEEN '1980' AND '1982'
- 查询各个管理人员下员工的平均工资,其中最低工资不能低于 1300,不计算老板
SELECT AVG(SAL), MGR,MIN(SAL) AS minsal FROM emp GROUP BY MGR HAVING MGR IS NOT NULL AND minsal>=1300
7、单行函数(了解)
1、日期函数
- NOW():获取当前时间;
- DAY(date):获取日期中的天数,范围是从 1 到 31;
- HOUR(time):返回 time 对应的小时数,范围是从 0 到 23;
- MINUTE(time):返回 time 对应的分钟数,范围是从 0 到 59;
- MONTH(date):返回 date 对应的月份,范围时从 1 到 12;
- YEAR(date):返回 date 对应的年份,范围是从 1000 到 9999;
- LAST_DAY(date):获取一个日期或日期时间值,返回该月最后一天对应的值。
8、子查询(掌握)
1、定义和作用
【定义】: 子查询指的就是在一个查询之中嵌套了其他若干查询。
【作用】: 在使用查询的时候,会出现限制条件不是确定的值,而是来源于一个查询结果。
【语法】:
SELECT select_list
FROM table
WHERE expr operator (SELECt select_list FROM table)
【使用注意事项】:
- 子查询一般出现在form和where子句中
- 子查询要使用圆括号括起来
- 将子查询放在比较运算符的右边
- 子查询在主查询执行之前先执行一次,主查询使用子查询的结果,但不宜嵌套过多。
2、分类
- 单行单列:只包含一个字段的查询,返回的查询结果也只包含一行数据。
- 多行单列:只包含一个字段的查询,返回的查询结果可能是多行或者零行。
- 多行多列:包含多个字段的返回,查询结果可能是单行或者多行。
【举例】:
- 查询出工资比 MARTIN 还要高的全部雇员信息
SELECT * FROM emp WHERE sal > (SELECT sal FROM emp WHERE ename = 'MARTIN')
- 查询平均工资高于公司平均工资的部门信息
SELECT e.deptno, d.dname, AVG(sal)
FROM emp e JOIN dept d ON e.deptno = d.deptno
GROUP BY deptno HAVING AVG(sal) >= (SELECT AVG(sal) FROM emp)
- 查询出每个部门的编号、名称、部门人数、平均工资
SELECT dept.deptno, temp.count, temp.avg
FROM dept JOIN (SELECT deptno dno, COUNT(empno) count, AVG(sal) avg FROM emp GROUP BY deptno) temp ON dept.deptno = temp.dno