MySQL学习篇——CRUD 多表操作

学习知识XMind图解:

MySQL学习篇——CRUD 多表操作_第1张图片

mysql 多表查询练习

分析图如下:

MySQL学习篇——CRUD 多表操作_第2张图片

1 多表的关系及创建

1.1 多表之间的关系

关联关系:在实际开发中,需要根据实体的内容设计数据表,实体间会有各种关联关系,所以根据实体设计的数据表之间也存在着各种关联关系,MySQL中数据表的关联关系有三种,具体如下:

- 一对多/多对一
多对一是数据表中最常见的一种关系,比如,员工和部门之间的关系,一个部门可以有多个员工,而一个员工不能属于多个部门,也就是说部门表中的一行在员工表中可以有许多匹配行,但员工表中的一行在在部门表中只能有一个匹配行。表之间的关系通过外键建立的,在多对一的表关系中,应该将外键建在多的一方,否则造成数据的冗余。
- 多对多
多对多也是数据表中的一种关系,比如学生与课程之间的关系,一个学生可以选择多门课程,当然一门课程也供多个学生选择,也就是说学生表中的一行在课程表中可以有许多匹配行,课程表中的一行在学生表中的也有许多匹配行。通常情况下,为了实现这种关系,需要定义一张中间表(称为连接表),该表会存在两个外键,分别参考课程表和学生表,在多对多关系中,需要注意的是,连接表的两个外键都是可以重复的,但是两个外键之间的关系是不能重复的,所以这两个外键又是连接表的联合主键
- 一对一
一对一关系在实际生活中比较常见,例如人与身份证之间就是一对一的关系,一个人对应一张身份证,一张身份证只能匹配一个人,那么一对一关系的两张表如何建立外键?
首先要分清主从关系,从表需要主表的存在才有意义,身份证需要人的存在才有意义。因此人为主表,身份证为从表,要在身份证中建立外键,由实际的经验可知,身份证中的外键必须是非空唯一的,因此会直接用从表(表身份证)的主键作为外键。

1.2 多表之间的关系如何来维护

– 数据库的五大约束:

 主键约束: primary key 唯一 / 非空
    唯一约束 : unique
    非空约束 : not null
    检查约束: mysql里面不支持 check
    外键约束: foreign key

–外键约束(多表约束): 外键是指引用另一张表中的一列或多列,被引用的列应该具有主键约束或唯一性约束.外键用于建立和加强两个表数据之间的连接,实际上是用来约束两张表,约束从表中的记录必须存在于主表中

作用:就是确保表中数据的完整性
从表:商品表依赖于分类表,所以商品称为从表
主表:分类表
书写语法:

-- 添加外键约束方式一 语法格式
alter table 表名 add foreign key(外键字段名) references 主表表名(主键字段名);

--添加外键约束方式二 建表时候就添加外键 语法格式
foreign key(列名) references 表名(列名)

注意:

  • 插入数据: 数据必须先存在于主表,然后再插入从表中的数据
  • 删除主表数据时候: 必须先删除从表中已引用了主表中数据,再删除主表中的数据
  • 删除表的时候,如果有从表里面有外键约束,必须先删除从表再删除主表
  • 从表在添加外键约束的时候,外键通常是主表中的主键

1.3 多表之间的建表原则

- 一对多:
  - 建表原则: 在多的一方添加一个键,然后让它作为外键指向一的一方
  - 例如:学生和班级,一个班级对多个学生,一个学生只属于一个班级,那么就可以在学生表里面建立外键指向班级表的主键
  - 商品分类和商品,一个分类下可以有多个商品,一个商品只属于一个分类,那么就可以在商品表里面建立外键指向商品分类表
- 多对多: 
  - 建表原则:创建一张中间表,将多对多的关系,拆分成一对多的关系, 中间表至少要包含两个外键,这两个外键分别指向各自原来的表主键
  - 例如:学生和选课
- 一对一:  
  - 建表原则: 1. 将两张表的主键建立连接, 2. 将两张合并成一张表 3. 将一对一的关系,拆分成一对多的关系
  - 合并表,拆表
  - 例如公民和身份证号

2 多表查询

概述:在关系型数据库管理系统中,建立表时各个数据之间的关系不必确定,通常将每个实体的所有信息存放在一个表中,当查询数据时候,通过连接操作查询多个表中的实体信息,当两个或多个表中存在相同意义的字段时候,便可以通过这些字段对不同的表进行连接查询,连接查询包括交叉连接查询,内连接查询,外连接查询.

交叉连接查询

概述:交叉连接返回的结果是被连接的两个表中所有数据行的笛卡尔积,也就是返回第一个表中符合查询条件的数据行乘以第二个表中符合查询条件的数据行数,例如 department表中有4个部门,employee表中有4个员工,那么交叉连接的结果有4*4=16条数据,注意:交叉查询得到的是笛卡尔积 : 两张表的乘积, 实际没有意义

-- 交叉连接的语法格式如下:
select * from 表1 cross join 表2;
select * from 表1,表2;
-- 以上语法格式 cross join 用于连接两个要查询的表,通过该语句可以查询两个表中所有的数据组合

-- 交叉查询
SELECT * FROM USER CROSS JOIN orders;
SELECT * FROM USER,orders;

内连接查询:

概述:

 内连接(inner join) 又称为简单连接或自然连接,是一种常见的连接查询,内连接使用比较运算符对两个表中的数据进行比较,并列出与连接条件匹配的数据行,组合成新的结果,也就是说在内连接查询中,只有满足条件的记录才能出现在查询结果中,内连接查询的语法如下:

内连接查询语法:

显式内连接: select 查询字段 from 表 1 inner join 表2 on 表1.关系字段 = 表2.关系字段; -- inner 可以省略不写
隐式内连接: select 查询字段 from 表1,表2 where 条件;

注意:

- inner join 用于连接两个表
- on 用于指定连接条件
- 其中inner 可以省略

 例如需求: 查找出1号用户 下了多少订单

            -- 显式内连接
        SELECT * FROM USER INNER JOIN orders ON user.userid = orders.uno WHERE user.userid = 1;
        -- 省略inner
        SELECT * FROM USER JOIN orders ON user.userid = orders.uno WHERE user.userid = 1;
        -- 隐式内连接
        SELECT * FROM USER,orders WHERE user.userid = orders.uno 
        -- 分析: 从 user表和订单表orders里面查询数据
        SELECT * FROM USER,orders WHERE user.userid = orders.uno AND user.username='zs';
        -- 需求: 查找出1号用户 下了多少订单

        -- 查找1号用户下了多少订单  用户表  订单表
        SELECT * FROM USER INNER JOIN orders ON orders.uno = user.userid WHERE user.userid=1
        SELECT * FROM USER INNER JOIN orders ON orders.uno = user.userid AND user.userid=1
        -- 起别名查询
        SELECT * FROM USER AS u,orders AS o WHERE o.uno = u.userid AND u.userid=1;
        SELECT * FROM USER u,orders o WHERE o.uno = u.userid AND u.userid=1;
        -- 隐式查询
        SELECT * FROM USER,orders WHERE orders.uno = user.userid AND user.userid=1

外连接查询:

概述:

 内连接查询中,返回的结果只包含符合查询条件和连接条件的数据,然而有时还需要包含没有关联的数据,即返回查询结果中不仅包含符合条件的数据,而且还包括左表(左连接或左外连接)/右表(右连接或右外连接)或者两个表(全外连接)中的所有数据,此时就需要使用外连接查询,外链接分为左连接和右连接.
简单理解为: A 表 , B表  交集C
内连接查询:两个表之间的交集(共有的部分)
外连接查询:1、左外连接查询 -->关联左表中的全部数据  2、右外连接查询-->关联右表中的全部数据

外连接语法:

select 所查字段 from 表 1 left|right [outer] join 表2 on 表1.关系字段 = 表2.关系字段 where 条件;

- left join(左外连接): 以左表为基础,将左表中所有的记录都查询出来,如果没有对应的记录,用null值填充
- Right join(右外连接):以右表为基础,将右表中的所有记录都查询出来,如果没有对应的记录,用null值填充
- ![这里写图片描述](https://img-blog.csdn.net/20170825141625288?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdTAxMDg0NzE0OA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
     -- 例如使用外连接查询
        -- 左外查询 
        -- 使用左外查询查用户和订单表,即使用户表中有用户没有购买任何商品,也会显式出来
        SELECT * FROM USER LEFT OUTER JOIN orders ON user.userid = orders.uno;
        -- 右外查询
        -- 使用右外查询查用户和订单表,即使用户表中有用户没有购买任何商品,也会显式出来
        SELECT * FROM orders RIGHT OUTER JOIN USER ON user.userid = orders.uno;

其他查询:分页查询 : limit

limit : 起始索引, 查询几条; 索引是0开始
–请听题: 每页显示3条数数据, 请查询出第2页的所有数据

    select * from product limit 0,3;  --第1页
    select * from product limit 3,3; -- 第2 页

起始索引: (页数-1)*每页显示数量 ,页数是从1开始
(3-1)*3
(4-1)*3
select * from product limit 6,3;

多表查询练习

1.查询出(商品名称product,商品分类名称category)信息

    SELECT * FROM product,category  WHERE product.cno = category.cid;
    SELECT * FROM product p,category c WHERE p.cno = c.cid;
    SELECT product.pname,category.cname FROM product,category  WHERE product.cno = category.cid;
    SELECT p.pname AS 商品名称,c.cname AS 商品类别 FROM product p,category c WHERE p.cno = c.cid;
    SELECT pname AS 商品名称,cname AS 商品类别 FROM product p,category c WHERE p.cno = c.cid;

2.查询分类名称为手机数码(category)的所有商品(product)

    SELECT pname AS 商品名称,cname AS 商品类别 FROM product p,category c WHERE p.cno = c.cid AND c.cname = '手机数码';
  1. 按照商品分类的名称统计商品的个数: count group by
    select cname 商品类别名称,count(1) from category c,product p where c.cid = p.cno group by c.cname ;
    select cname 商品类别名称,count(*) from category c,product p where c.cid = p.cno group by c.cname ;

4.查询1号订单的订单项信息(orderitem)和商品信息(product)

    -- 查询1号订单的订单项信息(orderitem)和商品信息(product)
    SELECT * FROM orderitem,product WHERE orderitem.pno = product.cno AND orderitem.ono = 1;
    SELECT * FROM orderitem oi,product p WHERE oi.ono = p.cno AND oi.ono = 1;

单表/多表巩固加强练习

员工信息表

    --员工信息表
    CREATE TABLE emp(
        empno INT,          /*员工编号*/
        ename VARCHAR(50),  /*员工姓名*/
        job VARCHAR(50),    /*员工工作*/
        mgr INT,            /*员工经理编号*/
        hiredate DATE,      /*员工入职日期*/
        sal DECIMAL(7,2),   /*员工工资*/
        comm DECIMAL(7,2),  /*员工奖金*/
        deptno INT          /*员工所处的部门编号*/
    );

    INSERT INTO emp values(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
    INSERT INTO emp values(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
    INSERT INTO emp values(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
    INSERT INTO emp values(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
    INSERT INTO emp values(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
    INSERT INTO emp values(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
    INSERT INTO emp values(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);
    INSERT INTO emp values(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);
    INSERT INTO emp values(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
    INSERT INTO emp values(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
    INSERT INTO emp values(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);
    INSERT INTO emp values(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
    INSERT INTO emp values(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
    INSERT INTO emp values(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);
    INSERT INTO emp values(7981,'MILLER','CLERK',7788,'1992-01-23',2600,500,20);

部门信息表

--部门表
    CREATE TABLE dept(
        deptno  INT,            /*部门的编号*/
        dname   varchar(14),    /*部门的名称*/
        loc varchar(13)         /*部门所处的位置*/
    );

    INSERT INTO dept values(10, 'ACCOUNTING', 'NEW YORK');
    INSERT INTO dept values(20, 'RESEARCH', 'DALLAS');
    INSERT INTO dept values(30, 'SALES', 'CHICAGO');
    INSERT INTO dept values(40, 'OPERATIONS', 'BOSTON');

基本查询

    -- 所有员工的信息
    SELECT * FROM emp;
    -- 薪资大于等于1000并且小于等于2000的员工信息
    SELECT * FROM emp WHERE sal >= 1000 AND sal <= 2000;
    SELECT * FROM emp WHERE sal BETWEEN 1000 AND  2000;
    -- 从员工表中查询出所有的部门编号
    SELECT DISTINCT deptno FROM emp;
    -- 查询出emp表中所有的工作种类(无重复)
    SELECT DISTINCT job FROM emp;
    -- 查询出名字以A开头的员工的信息
    SELECT * FROM emp WHERE ename LIKE 'A%';
    -- 查询出名字第二个字母是L的员工信息
    SELECT * FROM emp WHERE ename LIKE '_L%';
    -- 查询出名字中有“A”字符,并且薪水在1000以上(不包括1000)的所有员工信息
    SELECT * FROM emp WHERE ename LIKE '%A%'AND sal > 1000; 

    -- 查询出没有奖金的员工信息
    SELECT * FROM emp WHERE comm IS NULL;
    -- 所有员工的平均工资
    SELECT AVG(sal) AS 平均工资 FROM emp;
    -- 所有员工的工资总和
    SELECT SUM(sal) 工资总和 FROM emp;
    -- 所有员工的数量
    SELECT COUNT(1) FROM emp;
    -- 最高工资
    SELECT MAX(sal) 最高工资 FROM emp;
    -- 最少工资
    SELECT MIN(sal) 最低工资 FROM emp;

    -- 将所有员工按薪水升序排序,薪水相同的按照入职时间降序排序
    SELECT * FROM emp ORDER BY sal ASC,hiredate DESC;
    -- 查询出薪水在800到2500之间(闭区间)所有员工的信息。(注:使用两种方式实现and以及between and)
    SELECT * FROM emp WHERE sal >=800 AND sal <= 2500;
    SELECT * FROM emp WHERE sal BETWEEN 800 AND 2500;
    -- 查询出emp表中部门编号为20,薪水在2000以上(不包括2000)的所有员工,显示他们的员工号,姓名以及薪水,以如下列名显示:员工编号 员工名字 薪水
    -- 员工编号 员工名字 薪水
    SELECT empno 员工编号,ename 员工名字,sal 薪水 FROM emp WHERE sal >2000 AND deptno = 20;

    -- 查询出员工号为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);

分组查询

    -- 每个部门的平均工资
    SELECT deptno 部门编号,AVG(sal) 平均薪资 FROM emp GROUP BY deptno ;
    -- 列出在每个部门工作的员工数量、平均工资。
    SELECT deptno 部门编号,COUNT(*) 员工数量 ,AVG(sal) 平均薪资 FROM emp GROUP BY deptno;
    -- 列出各种工作的最低工资
    SELECT job,MIN(sal) FROM emp GROUP BY job;
    -- 列出最低薪金大于1500的各种工作
    SELECT job,MIN(sal) FROM emp GROUP BY job HAVING MIN(sal)>1500;

多表查询

-- 列出所有员工的姓名、部门名称和工资。
SELECT emp.ename 员工姓名,dept.dname 部门名称,emp.sal 工资 FROM emp,dept WHERE emp.deptno = dept.deptno;

-- 查询员工编号,员工姓名,经理编号,经理姓名
SELECT e1.empno 员工编号,e1.ename 员工姓名,e1.mgr 经理编号,e2.ename 经理姓名 FROM emp e1,emp e2 WHERE e1.mgr = e2.empno;

-- 查询员工编号,员工姓名,员工部门名称,经理编号,经理姓名
SELECT e1.empno 员工编号,e1.ename 员工姓名,d.dname 部门名称, e1.mgr 经理编号,e2.ename 经理姓名 FROM emp e1,emp e2,dept d WHERE e1.mgr = e2.empno AND d.deptno = e1.deptno;

你可能感兴趣的:(mysql)