MySQL 之 数据操作:多表查询

目录

一.多表联合查询

二 多表连接查询

三 复杂条件多表查询 

四 子语句查询(嵌套查询)

五、其他查询

5.1 临时表查询

5.2 判断查询   IF 关键字(面试题)

六、SQL逻辑查询语句执行顺序(重点***)

七、外键约束

八 、其他约束类型

九、数据库设计三范式

1.第一范式(确保每列保持原子性)

2.第二范式(确保表中的每列都和主键相关)

3.第三范式(确保每列都和主键列直接相关,而不是间接相关)



多表查询的栗子会用到以下创建出来的数据:(直接复制到Navicat中生成就好了)(不生成也可以直接看我截图就好)

#创建部门
CREATE TABLE IF NOT EXISTS dept (
    did int not null auto_increment PRIMARY KEY,
    dname VARCHAR(50) not null COMMENT '部门名称'
)ENGINE=INNODB DEFAULT charset utf8;


#添加部门数据
INSERT INTO `dept` VALUES ('1', '教学部');
INSERT INTO `dept` VALUES ('2', '销售部');
INSERT INTO `dept` VALUES ('3', '市场部');
INSERT INTO `dept` VALUES ('4', '人事部');
INSERT INTO `dept` VALUES ('5', '鼓励部');

-- 创建人员
DROP TABLE IF EXISTS `person`;
CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(50) NOT NULL,
  `age` tinyint(4) DEFAULT '0',
  `sex` enum('男','女','人妖') NOT NULL DEFAULT '人妖',
  `salary` decimal(10,2) NOT NULL DEFAULT '250.00',
  `hire_date` date NOT NULL,
  `dept_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8;

-- 添加人员数据

-- 教学部
INSERT INTO `person` VALUES ('1', 'alex', '28', '人妖', '53000.00', '2010-06-21', '1');
INSERT INTO `person` VALUES ('2', 'wupeiqi', '23', '男', '8000.00', '2011-02-21', '1');
INSERT INTO `person` VALUES ('3', 'egon', '30', '男', '6500.00', '2015-06-21', '1');
INSERT INTO `person` VALUES ('4', 'jingnvshen', '18', '女', '6680.00', '2014-06-21', '1');

-- 销售部
INSERT INTO `person` VALUES ('5', '歪歪', '20', '女', '3000.00', '2015-02-21', '2');
INSERT INTO `person` VALUES ('6', '星星', '20', '女', '2000.00', '2018-01-30', '2');
INSERT INTO `person` VALUES ('7', '格格', '20', '女', '2000.00', '2018-02-27', '2');
INSERT INTO `person` VALUES ('8', '周周', '20', '女', '2000.00', '2015-06-21', '2');

-- 市场部
INSERT INTO `person` VALUES ('9', '月月', '21', '女', '4000.00', '2014-07-21', '3');
INSERT INTO `person` VALUES ('10', '安琪', '22', '女', '4000.00', '2015-07-15', '3');

-- 人事部
INSERT INTO `person` VALUES ('11', '周明月', '17', '女', '5000.00', '2014-06-21', '4');

-- 鼓励部
INSERT INTO `person` VALUES ('12', '苍老师', '33', '女', '1000000.00', '2018-02-21', null);

创建表和数据

代码意思是:创建2张表dept和person,创建后截图:

部门表:

员工表:

一.多表联合查询

#多表查询语法:

select  字段1,字段2... from 表1,表2... [where 条件]

查询人员和部门所有信息

select * from person,dept where person.dept_id = dept.did

#注意: 多表查询时,一定要找到两个表中相互关联的字段,并且作为条件使用

图示:

二 多表连接查询

多表连接查询语法(重点):


SELECT 字段列表 FROM 表1 INNER|LEFT|RIGHT JOIN 表2 ON 表1.字段 = 表2.字段;
  •  内连接查询 (只显示符合条件的数据)

栗子:查询人员和部门所有信息

select * from person inner join dept on person.dept_id =dept.did;

 效果: 大家可能会发现, 内连接查询与多表联合查询的效果是一样的.

  • 左外连接查询 (左边表中的数据优先全部显示)

栗子:查询人员和部门所有信息

select * from person left join dept on person.dept_id =dept.did;

效果:人员表中的数据全部都显示,而 部门表中的数据符合条件的才会显示,不符合条件的会以 null 进行填充.

  • 右外连接查询 (右边表中的数据优先全部显示)

栗子:查询人员和部门所有信息

select * from person RIGHT JOIN dept on person.dept_id = dept.did

效果:正好与[左外连接相反]

  • 全连接查询(显示左右表中全部数据)

全连接查询:是在内连接的基础上增加 左右两边没有显示的数据

注意: mysql并不支持全连接 full JOIN 关键字
注意: 但是mysql 提供了 UNION 关键字.使用 UNION 可以间接实现 full JOIN 功能

栗子:查询人员和部门的所有数据

select * from person LEFT JOIN dept on person.dept_id = dept.did
UNION 
select * from person RIGHT JOIN dept on person.dept_id = dept.did

三 复杂条件多表查询 

1. 查询出 教学部 年龄大于20岁,并且工资小于40000的员工,按工资倒序排列.

select * from person where age>20 and 
dept_id =(select did from dept where dname ='教学部') and salary <10000 ORDER by salary DESC

2.查询每个部门中最高工资和最低工资是多少,显示部门名称

select MAX(salary),min(salary),dname from person 
LEFT JOIN dept ON person.dept_id = dept.did GROUP BY dept_id

四 子语句查询(嵌套查询)

子查询(嵌套查询): 查多次, 多个select

注意: 第一次的查询结果可以作为第二次的查询的 条件 或者 表名 使用.

子查询中可以包含:IN、NOT IN、ANY、ALL、EXISTS 和 NOT EXISTS等关键字. 还可以包含比较运算符:= 、 !=、> 、<等.

  •  作为表名使用
select * from (select * from person) as 表名;

ps:大家需要注意的是: 一条语句中可以有多个这样的子查询,在执行时,最里层括号(sql语句) 具有优先执行权.<br>注意: as 后面的表名称不能加引号('')

  • 求最大工资那个人的姓名和薪水
1.求最大工资
select max(salary) from person;
2.求最大工资那个人叫什么
select name,salary from person where salary=53000;

合并
select name,salary from person where salary=(select max(salary) from person);
  • 求工资高于所有人员平均工资的人员
1.求平均工资
select avg(salary) from person;

2.工资大于平均工资的 人的姓名、工资
select name,salary from person where salary > 21298.625;

合并
select name,salary from person where salary >(select avg(salary) from person);
  • 关键字(基本很少用)

ANY关键字

假设any内部的查询语句返回的结果个数是三个,如:result1,result2,result3,那么,

select ...from ... where a > any(...);
->
select ...from ... where a > result1 or a > result2 or a > result3;

ALL关键字

ALL关键字与any关键字类似,只不过上面的or改成and。即:

select ...from ... where a > all(...);
->
select ...from ... where a > result1 and a > result2 and a > result3;

SOME关键字

some关键字和any关键字是一样的功能。所以:

select ...from ... where a > some(...);
->
select ...from ... where a > result1 or a > result2 or a > result3;

EXISTS 关键字

EXISTS 和 NOT EXISTS 子查询语法如下:

  SELECT ... FROM table WHERE  EXISTS (subquery)
该语法可以理解为:主查询(外部查询)会根据子查询验证结果(TRUE 或 FALSE)来决定主查询是否得以执行。

mysql> SELECT * FROM person WHERE EXISTS (SELECT * FROM dept WHERE did=5);
Empty set (0.00 sec)
此处内层循环并没有查询到满足条件的结果,因此返回false,外层查询不执行。

NOT EXISTS刚好与之相反

五、其他查询

  • 5.1 临时表查询

需求:  查询高于本部门平均工资的人员

   解析思路: 1.先查询本部门人员平均工资是多少.

         2.再使用人员的工资与部门的平均工资进行比较

错误思路:

select * from person GROUP BY dept_id HAVING salary > (select avg(salary) from person group by dept_id)
-- avg(salary)是多个薪水的集合无法和salary比较 

正确思路:

-- 1.先查询部门人员的平均工资
SELECT dept_id,AVG(salary)as sal from person GROUP BY dept_id;
 
-- 2.再用人员的工资与部门的平均工资进行比较
SELECT * FROM person as p1,
    (SELECT dept_id,AVG(salary)as '平均工资' from person GROUP BY dept_id) as p2
where p1.dept_id = p2.dept_id AND p1.salary >p2.平均工资;

ps:在当前语句中,我们可以把上一次的查询结果当前做一张表来使用.因为p2表不是真是存在的,所以:我们称之为 临时表  
   临时表:不局限于自身表,任何的查询结果集都可以认为是一个临时表.
  • 5.2 判断查询   IF 关键字(面试题)

单条件:

语法: IF(条件表达式,"结果为true",'结果为false');

需求1 :根据工资高低,将人员划分为两个级别,分别为 高端人群和低端人群。显示效果:姓名,年龄,性别,工资,级别

select *,if(salary>10000,'高端人群','低端人群') as '级别' from person

多条件:

语法一:

SELECT
    CASE WHEN STATE = '1' THEN '成功'
         WHEN STATE = '2' THEN '失败'
         ELSE '其他' END 
FROM 表;

语法二:

SELECT CASE age(要判断的表头)
           WHEN 23 THEN '23岁'
           WHEN 27 THEN '27岁'
           WHEN 30 THEN '30岁'
        ELSE '其他岁' END
FROM person(表明);

需求2: 根据工资高低,统计每个部门人员收入情况,划分为 富人,小资,平民,吊丝 四个级别, 要求统计四个级别分别有多少人

select dname ,
	sum(case when person.salary > 10000 then 1 else 0 end) as '富人' ,
	sum(case when person.salary between 5000 and 10000 then 1 else 0 end) as '小资' ,
	sum(case when person.salary between 3000 and 5000 then 1 else 0 end) as '平民' ,
	sum(case when person.salary < 3000 then 1 else 0 end) as '屌丝' 

from dept,person where dept.did = person.dept_id group by dept_id

六、SQL逻辑查询语句执行顺序(重点***)

执行顺序:

(7)     SELECT 
(8)     DISTINCT <select_list>
(1)     FROM <left_table>
(3)     <join_type> JOIN <right_table>
(2)     ON <join_condition>
(4)     WHERE <where_condition>
(5)     GROUP BY <group_by_list>
(6)     HAVING <having_condition>
(9)     ORDER BY <order_by_condition>
(10)    LIMIT <limit_number>

七、外键约束

1.问题?

  什么是约束:约束是一种限制,它通过对表的行或列的数据做出限制,来确保表的数据的完整性、唯一性

2.问题?

  以上两个表 person和dept中, 新人员可以没有部门吗?

3.问题?

  新人员可以添加一个不存在的部门吗?

4.如何解决以上问题呢?

  简单的说,就是对两个表的关系进行一些约束 (即: froegin key). 

  foreign key 定义:就是表与表之间的某种约定的关系,由于这种关系的存在,能够让表与表之间的数据,更加的完整,关连性更强。

5.具体操作

  • 创建表时,同时创建外键约束

这个部分MySQL版本不同,语法略有不同

  • 已经创建表后,追加外键约束
-- 添加外键约束
ALTER table person add constraint fk_did FOREIGN key(dept_id) REFERENCES dept(did);
 
-- 删除外键约束
ALTER TABLE person drop FOREIGN key fk_did;

定义外键的条件:

(1)外键对应的字段数据类型保持一致,且被关联的字段(即references指定的另外一个表的字段),必须保证唯一

(2)所有tables的存储引擎必须是InnoDB类型.(5.7版本后默认都是InnoDB类型)

(3)外键的约束4种类型: 1.RESTRICT 2. NO ACTION 3.CASCADE 4.SET NULL

RESTRICT
同no action, 都是立即检查外键约束

NO ACTION
如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作  

CASCADE
在父表上update/delete记录时,同步update/delete掉子表的匹配记录 

SET NULL
在父表上update/delete记录时,将子表上匹配记录的列设为null (要注意子表的外键列不能为not null)  

(4)建议:1.如果需要外键约束,最好创建表同时创建外键约束.

       2.如果需要设置级联关系,删除时最好设置为 SET NULL.

注:插入数据时,先插入主表中的数据,再插入从表中的数据。

       删除数据时,先删除从表中的数据,再删除主表中的数据。

 

八 、其他约束类型

1 非空约束

关键字: NOT NULL ,表示 不可空. 用来约束表中的字段列

create table t1(
       id int(10) not null primary key,
       name varchar(100) null
 ); 

2.主键约束

 用于约束表中的一行,作为这一行的标识符,在一张表中通过主键就能准确定位到一行,因此主键十分重要。

注意: 主键这一行的数据不能重复不能为空

create table t2(
    id int(10) not null primary key
);

还有一种特殊的主键——复合主键。主键不仅可以是表中的一列,也可以由表中的两列或多列来共同标识

create table t3(
    id int(10) not null,
    name varchar(100) ,
    primary key(id,name)
);

3.唯一约束

 关键字: UNIQUE, 比较简单,它规定一张表中指定的一列的值必须不能有重复值,即这一列每个值都是唯一的。

栗子:

create table t4(
    id int(10) not null,
    name varchar(255) ,
    unique id_name(id,name)
);
-- 添加唯一约束
alter table t4 add unique id_name(id,name);
-- 删除唯一约束
alter table t4 drop index id_name;

注意: 当INSERT语句新插入的数据和已有数据重复的时候,如果有UNIQUE约束,则INSERT失败.

4.默认值约束  

关键字: DEFAULT

INSERT语句执行时.,如果被DEFAULT约束的位置没有值,那么这个位置将会被DEFAULT的值填充

栗子:

create table t5(
    id int(10) not null primary key,
    name varchar(255) default '张三'   
);
-- 插入数据
INSERT into t5(id) VALUES(1),(2);

九、数据库设计三范式

1.第一范式(确保每列保持原子性)

第一范式是最基本的范式。如果数据库表中的所有字段值都是不可分解的原子值,就说明该数据库表满足了第一范式。

第一范式的合理遵循需要根据系统的实际需求来定。比如某些数据库系统中需要用到“地址”这个属性,本来直接将“地址”属性设计成一个数据库表的字段就行。但是如果系统经常会访问“地址”属性中的“城市”部分,那么就非要将“地址”这个属性重新拆分为省份、城市、详细地址等多个部分进行存储,这样在对地址中某一部分操作的时候将非常方便。这样设计才算满足了数据库的第一范式,如下表所示。

 

2.第二范式(确保表中的每列都和主键相关)

第二范式在第一范式的基础之上更进一层。第二范式需要确保数据库表中的每一列都和主键相关,而不能只与主键的某一部分相关(主要针对联合主键而言)。也就是说在一个数据库表中,一个表中只能保存一种数据,不可以把多种数据保存在同一张数据库表中。

比如要设计一个订单信息表,因为订单中可能会有多种商品,所以要将订单编号和商品编号作为数据库表的联合主键,如下表所示。

这样就产生一个问题:这个表中是以订单编号和商品编号作为联合主键。这样在该表中商品名称、单位、商品价格等信息不与该表的主键相关,而仅仅是与商品编号相关。所以在这里违反了第二范式的设计原则。

而如果把这个订单信息表进行拆分,把商品信息分离到另一个表中,把订单项目表也分离到另一个表中,就非常完美了。如下所示。

3.第三范式(确保每列都和主键列直接相关,而不是间接相关)

第三范式需要确保数据表中的每一列数据都和主键直接相关,而不能间接相关。

比如在设计一个订单数据表的时候,可以将客户编号作为一个外键和订单表建立相应的关系。而不可以在订单表中添加关于客户其它信息(比如姓名、所属公司等)的字段。如下面这两个表所示的设计就是一个满足第三范式的数据库表。

注意事项:

1.第二范式与第三范式的本质区别:在于有没有分出两张表。

第二范式是说一张表中包含了多种不同实体的属性,那么必须要分成多张表,第三范式是要求已经分好了多张表的话,一张表中只能有另一张标的ID,而不能有其他任何信息,(其他任何信息,一律用主键在另一张表中查询)。

2.必须先满足第一范式才能满足第二范式,必须同时满足第一第二范式才能满足第三范式。

猜你喜欢

转载自blog.csdn.net/qq_35883464/article/details/86479396