多表查询与事物--(二)

三范式

1:什么是范式:

--范式是指:设计数据库表的规则(Normal Form) 好的数据库设计对数据的存储性能和后期的程序开发,都会产生重要的影响。建立科学的,规范的数据库就需要满足一些规则来优化数据的设计和存储

2.范式的分类

--范式的基本 : 目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-	科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范		式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。

一般说来,数据库只需满足第三范式(3NF)就行了

第一范式

    --即数据库表的每一列都是不可分割的原子数据项,而不能是集合、数组、记录等非原子数据项。即实体中的某个属性有多个值时,必须拆分为不同的属性。在符合第一范式(1NF)表中每个列的值只能是表的一个属性或一个属性的一部分。简而言之,第一范式每一列不可再拆分,称为原子性。 第一范式:每一列不能再拆分
    

总结:如果不遵守第一范式,查询出数据还需要进一步处理(查询不方便)。遵守第一范式,需要什么字段的数据就查询什么数据(方便查询)。

第二范式

    --第二范式(2NF)要求数据库表中的每个实例或记录必须可以被唯一地区分。选取一个能区分每个实体的属性或属性组,作为实体的唯一标识。例如在员工表中的身份证号码即可实现每个员工的区分,该身份证号码即为候选键,任何一个候选键都可以被选作主键。在找不到候选键时,可额外增加属性以实现区分。 第二范式(2NF)要求实体的属性完全依赖于主关键字。所谓完全依赖是指不能存在仅依赖主关键字一部分的属性。如果存在,那么这个属性和主关键字的这一部分应该分离出来形成一个新的实体,新实体与原实体之间是一对多的关系。为实现区分通常需要为表加上一个列,以存储各个实例的唯一标识。简而言之,第二范式就是在第一范式的基础上属性完全依赖于主键。
    
    也就是一张表描述一件事
    
    

总结:如果不准守第二范式,数据冗余,相同数据无法区分。遵守第二范式减少数据冗余,通过主键区分相同数据.

第三范式

    --在2NF基础上,任何非主属性不依赖于其它非主属性(在2NF基础上消除传递依赖) 第三范式(3NF)是第二范式(2NF)的一个子集,即满足第三范式(3NF)必须满足第二范式(2NF)。简而言之,第三范式(3NF)要求一个关系中不包含已在其它关系已包含的非主关键字信息
    
    --从表的外键必须使用主表的主键

总结:如果不准守第三范式,可能会有相同数据无法区分,修改数据的时候多张表都需要修改(不方便修改)。遵守第三范式通过id可以区分相同数据,修改数据的时候只需要修改一张表(方便修改)。





多表查询

同时查询多张表获取到需要的数据 比如:我们想查询到开发部有多少人,需要将部门表和员工表同时进行查询

- 分类: 
  - 内连接
  - - 隐式内连接
    - 显示内连接
  - 外连接
  - - 左外连接
    - 右外连接

    --创建员工数据库
     create database employee;
    
    --创建部门表
    
      create table dept(
        -> id int primary key auto_increment ,
        -> name varchar(20)
        -> );
    --插入数据
    	insert into dept values (null,'开发部'),(null,'市场部'),(null,'财务部');
    --创建员工表
        mysql> create table emp (
            -> id int primary key auto_increment ,
            -> name varchar(10),   
            -> gender char(1),     	-- 性别
            -> salary double , 		--工资
            -> join_date date,		--入职日期
            -> dept_id int
            -> );
    --插入数据
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('孙悟空','男',7200,'2013‐02‐24',1);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('猪八戒','男',3600,'2010‐12‐02',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('唐僧','男',9000,'2008‐08‐08',2);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('白骨精','女',5000,'2015‐10‐07',3);
    INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('蜘蛛精','女',4500,'2011‐03‐14',1);
    
    
    

笛卡尔积现象

多表查询时左表的每条数据和右表的每条数据组合,这种效果成为笛卡尔积

需求:查询每个部门有哪些人

	SELECT * FROM dept, em

    --左边每条数据和右表的每条数据组合,这就是笛卡尔积现象
    mysql> select * from dept,emp;
    +----+--------+----+--------+--------+--------+------------+---------+
    | id | name   | id | name   | gender | salary | join_date  | dept_id |
    +----+--------+----+--------+--------+--------+------------+---------+
    |  1 | 开发部 |  1 | 孙悟空 | 男     |   7200 | 2013-02-24 |       1 |
    |  2 | 市场部 |  1 | 孙悟空 | 男     |   7200 | 2013-02-24 |       1 |
    |  3 | 财务部 |  1 | 孙悟空 | 男     |   7200 | 2013-02-24 |       1 |
    |  1 | 开发部 |  2 | 猪八戒 | 男     |   3600 | 2010-12-02 |       2 |
    |  2 | 市场部 |  2 | 猪八戒 | 男     |   3600 | 2010-12-02 |       2 |
    |  3 | 财务部 |  2 | 猪八戒 | 男     |   3600 | 2010-12-02 |       2 |
    |  1 | 开发部 |  3 | 唐僧   | 男     |   9000 | 2008-08-08 |       2 |
    |  2 | 市场部 |  3 | 唐僧   | 男     |   9000 | 2008-08-08 |       2 |
    |  3 | 财务部 |  3 | 唐僧   | 男     |   9000 | 2008-08-08 |       2 |
    |  1 | 开发部 |  4 | 白骨精 | 女     |   5000 | 2015-10-07 |       3 |
    |  2 | 市场部 |  4 | 白骨精 | 女     |   5000 | 2015-10-07 |       3 |
    |  3 | 财务部 |  4 | 白骨精 | 女     |   5000 | 2015-10-07 |       3 |
    |  1 | 开发部 |  5 | 蜘蛛精 | 女     |   4500 | 2011-03-14 |       1 |
    |  2 | 市场部 |  5 | 蜘蛛精 | 女     |   4500 | 2011-03-14 |       1 |
    |  3 | 财务部 |  5 | 蜘蛛精 | 女     |   4500 | 2011-03-14 |       1 |
    +----+--------+----+--------+--------+--------+------------+---------+
    
    
    --过滤掉没用的
    mysql>
    		select * from dept,emp where emp.dept_id = dept.id;
    		
    
    

内连接

用左边表的记录去匹配右边表的记录,如果符合条件的则显示

- 隐式内连接
  	隐式内连接:看不到 JOIN 关键字,条件使用 WHERE 指定 SELECT 字段名 FROM 左表, 右表 WHERE 条件;
- 显示内连接
  	显示内连接:使用 INNER JOIN ... ON 语句, 可以省略 INNER SELECT 字段名 FROM 左表 INNER JOIN 右表 ON 条件;



    --确定查询的有哪些表
    SELECT * FROM dept INNER JOIN emp;
    
    --1. 确定表连接条件,员工表.dept_id = 部门表.id 的数据才是有效的
    SELECT * FROM dept INNER JOIN emp ON emp.dept_id=dept.id;
    
    
    --1. 确定表连接条件,我们查询的是唐僧的信息,部门表.name='唐僧'
    SELECT * FROM dept INNER JOIN emp ON emp.dept_id=dept.id AND emp.NAME='唐僧';
    
    --1. 确定查询字段,查询唐僧的信息,显示员工id,姓名,性别,工资和所在的部门名称
    SELECT emp.id, emp.NAME, emp.gender, emp.salary, dept.NAME FROM dept INNER JOIN emp ONemp.dept_id=dept.id AND emp.NAME='唐僧';



总结:

总结内连接查询步骤:

1. 确定查询哪些表
2. 确定表连接条件
3. 确定查询字段



左外连接

左外连接:使用 LEFT OUTER JOIN ... ON , OUTER 可以省略 SELECT 字段名 FROM 左表 LEFT OUTER JOIN 右表 ON

条件; 用左边表的记录去匹配右边表的记录,如果符合条件的则显示;否则,显示NULL 可以理解为:在内连接的基础上保证左表的数据全部显示



- 部门表中增加一个销售部
      INSERT INTO dept (NAME) VALUES ('销售部');
- 使用内连接查询
      SELECT * FROM dept INNER JOIN emp ON emp.dept_id=dept.id;
- 使用左外连接查询
      SELECT * FROM dept LEFT OUTER JOIN emp ON emp.dept_id=dept.id;
      
      --用左边表的记录去匹配右边表的记录,如果符合条件的则显示;否则,显示NULL
      --可以理解为:在内连接的基础上保证左表的数据全部显示
  

右外连接

使用 RIGHT OUTER JOIN ... ON , OUTER 可以省略 SELECT 字段名 FROM 左表 RIGHT OUTER JOIN 右表 ON 条件; 

用右边表的记录去匹配左边表的记录,如果符合条件的则显示;否则,显示NULL 可以理解为:在内连接的基础上保证右表的数据全部显示

- 在员工表中增加一个员工
      INSERT INTO emp(NAME,gender,salary,join_date,dept_id) VALUES('沙僧','男',6666,'2013-02-24',NULL);
- 使用内连接查询
      SELECT * FROM dept INNER JOIN emp ON emp.dept_id =dept.id;
      
      --用左边表的记录去匹配右边表的记录,如果符合条件则显示
      
- 使用右外连接查询
      SELECT * FROM dept RIGHT OUTER JOIN emp ON emp.dept_id =dept.id;
      --用右表的记录去匹配左边表的记录,如果符合条件的则显示;否则NUll
      --可以理解为:在内连接的基础上保证右表的数据全部显示

子查询

---

 

一条SELECT语句结果作为另一条SELECT语法一部分(作为查询条件或查询结果或表) SELECT 查询字段 FROM 表WHERE 查询条件; 

SELECT * FROM employee WHERE salary=(SELECT MAX(salary) FROM employee);



    子查询需要放在()中
    子查询结果的三种情况:
    1. 子查询的结果是一个值的时候
    2. 子查询结果是单列多行的时候
    3. 子查询的结果是多行多列的时
    --说明: 子查询结果只要是 单列 ,肯定在 WHERE 后面作为 条件 子查询结果只要是 多列 ,肯定在 FROM 后面作为表;

子查询的结果是一个值

    --子查询结果只要是 单列 ,肯定在 WHERE 后面作为 条件 SELECT 查询字段 FROM 表 WHERE 字段=(子查询);
    -- 查询工资最高的员工是谁?
    1. 查询最高工资是多少
    SELECT MAX(salary) FROM emp;
    1. 根据最高工资到员工表查询到对应的员工信息
    SELECT * FROM emp WHERE salary=(SELECT MAX(salary) FROM emp);
    --查询工资小于平均工资的员工有哪些?
    1. 查询平均工资是多少
    SELECT AVG(salary) FROM emp;
    1. 到员工表查询小于平均的员工信息
    SELECT * FROM emp WHERE salary < (SELECT AVG(salary) FROM emp);

子查询结果是单例多行

    --子查询结果只要是 单列 ,肯定在 WHERE 后面作为 条件 子查询结果是单例多行,结果集类似于一个数组,父查询使用 IN 运算符 SELECT 查询字段 FROM 表 WHERE 字段 IN (子查询);
    
    --1. 查询工资大于5000的员工,来自于哪些部门的名字
    1. 先查询大于5000的员工所在的部门id
    SELECT dept_id FROM emp WHERE salary > 500;
    1. 再查询在这些部门id中部门的名字
    SELECT dept.name FROM dept WHERE dept.id IN (SELECT dept_id FROM emp WHERE salary > 5000);
    --2. 查询开发部与财务部所有的员工信息
    1. 先查询开发部与财务部的id
    SELECT id FROM dept WHERE NAME IN('开发部','财务部');
    1. 再查询在这些部门id中有哪些员工
    SELECT * FROM emp WHERE dept_id IN (SELECT id FROM dept WHERE NAME IN('开发部','财务部'));

子查询的结果是多行多列

子查询结果只要是 多列 ,肯定在 FROM 后面作为 表 SELECT 查询字段 FROM (子查询) 表别名 WHERE 条件; 子查询作为表需要取别名,否则这张表没用名称无法访问表中的字段



    查询出2011年以后入职的员工信息,包括部门名称
    1. 在员工表中查询2011-1-1以后入职的员工
    SELECT * FROM emp WHERE join_date > '2011‐1‐1';
    1. 查询所有的部门信息,与上面的虚拟表中的信息组合,找出所有部门id等于的dept_id
    SELECT * FROM dept d, (SELECT * FROM emp WHERE join_date > '2011‐1‐1') e WHERE e.dept_id =d.id;
    
    使用表连接:SELECT d.*, e.* FROM dept d INNER JOIN emp e ON d.id = e.dept_id WHERE e.join_date > '2011‐1‐1';



多表查询总结

    子查询结果只要是 单列 ,肯定在 WHERE 后面作为 条件
    SELECT 查询字段 FROM 表 WHERE 字段=(子查询);
    子查询结果只要是 多列 ,肯定在 FROM 后面作为 表
    SELECT 查询字段 FROM (子查询) 表别名 WHERE 条件;
    
    1. 不管我们查询几张表,表连接查询会产出笛卡尔积,我们需要消除笛卡尔积,拿到正确的数据。我们需要找到表与表之间通过哪个字段关联起来的(通常是 外键=主键 )
    2. 消除笛卡尔积规律:2张表需要1个条件,3张表需要2个条件,4张表需要3个条件。(条件数量=表的数量-1),每张表都要参与进来
    3. 多表连接查询步骤:
    	3.1. 确定要查询哪些表
    	3.2. 确定表连接条件
    	3.3. 确定查询



事务安全

事务的应用场景说明

在实际的业务开发中,有些业务操作要多次访问数据库。一个业务要发送多条SQL语句给数据库执行。需要将多次访问数据库的操作视为一个整体来执行,要么所有的SQL语句全部执行成功。如果其中有一条SQL语句失败,就进行事务的回滚,所有的SQL语句全部执行失败。 例如: 张三给李四转账,张三账号减钱,李四账号加钱

猜你喜欢

转载自blog.csdn.net/qq_32891657/article/details/82318210