一、多表关系
有以下三种关系:一对多,多对多,一对一
1、多表关系问题分析
在一个部门员工表下,部门数据可以直接删除,然而还有部分员工归属于该部门下,此时就出现了数据的不完整、不一致问题 。出现问题的原因是目前上述的两张表,在数据库层面,并未建立关联,所以是无法保证数据的一致性和完整性的 。想解决上述的问题呢,我们就可以通过数据库中的 外键约束 来解决。
通过SQL语句操作:
sql
-- 创建表时指定
create table 表名(
字段名 数据类型,
...
[constraint] [外键名称] foreign key (外键字段名) references 主表 (主表列名)
);
-- 建完表后,添加外键
alter table 表名 add constraint 外键名称 foreign key(外键字段名) references 主表(主表列名);
通过图形化界面操作:

外键约束(foreign key):保证了数据的完整性和一致性。
2、物理外键和逻辑外键
①物理外键
|--------|------------------------------------------------------------------------|
| 概念 | 使用foreign key定义外键关联另外一张表 |
| 缺点 | * 影响增、删、改的效率(需要检查外键关系)。 * 仅用于单节点数据库,不适用于分布式、集群场景。 * 容易引发数据库的死锁问题,消耗性能。 |
②逻辑外键
在业务层逻辑中,解决外键关联,通过逻辑外键,就可以很方便的解决上述问题(逻辑外键的操作过程通过代码层面(例如通过serviceimpl设置的if条件语句,如果存在/不存在,就可以/不可以执行这个操作))。在现在的企业开发中,很少会使用物理外键,都是使用逻辑外键。 甚至在一些数据库开发规范中,会明确指出禁止使用物理外键 foreign key。
二、多表查询
查询时从多张表中获取所需数据,进行多表查询时,只需要使用逗号分隔多张表即可,如: select 字段列表 from 表1, 表2,假如这样直接查询会出现很多无效的笛卡尔积(即所有的组合情况,有点数据重了),所以可以在尾部加上条件语句where
sql
select * from emp , dept where emp.dept_id = dept.id ;
1、连接查询
①内连接
相当于查询A、B交集部分数据,语法上分为显式内连接和隐式内连接
隐式内连接:
sql
select 字段列表 from 表1 , 表2 where 条件 ... ;
显式内连接:
sql
select 字段列表 from 表1 [ inner ] join 表2 on 连接条件 ... ;
在SQL语句中可以为表起别名简化书写:
sql
select 字段列表 from 表1 as 别名1 , 表2 as 别名2 where 条件 ... ;
select 字段列表 from 表1 别名1 , 表2 别名2 where 条件 ... ; -- as 可以省略
例子:
sql
select e.id, e.name, d.name from emp as e , dept as d where e.dept_id = d.id and e.gender = 1 and e.salary > 8000;
**注意:**一旦为表起了别名,就不能再使用表名来指定对应的字段了,此时只能够使用别名来指定字段。
②外连接
分为左连接和右连接,左外连接相当于查询表1(左表)的所有数据,当然也包含表1和表2交集。右外连接相当于查询表2(右表)的所有数据,当然也包含表1和表2交集部分的数据。部分的数据。
左连接语法:
sql
select 字段列表 from 表1 left [ outer ] join 表2 on 连接条件 ... ;
左外连接:以left join关键字左边的表为主表,查询主表中所有数据,以及和主表匹配的右边表中的数据
右连接语法:
sql
select 字段列表 from 表1 right [ outer ] join 表2 on 连接条件 ... ;
右外连接:以right join关键字右边的表为主表,查询主表中所有数据,以及和主表匹配的左边表中的数据
**注意:**左外连接和右外连接是可以相互替换的,只需要调整连接查询时SQL语句中表的先后顺序就可以了。而我们在日常开发使用时,更偏向于左外连接。
③子查询
SQL语句中嵌套select语句,称为嵌套查询,又称子查询。
sql
SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2 ... );
子查询外部的语句可以是insert / update / delete / select 的任何一个,最常见的是 select。
根据子查询的结果不同分为以下四种:
|-----------|--------------------------------------------------------------|
| 标量子查询 | 子查询结果为单个值 [一行一列],常用操作符:= <> > >= < <= |
| 列子查询 | 子查询结果为一列,但可以是多行,常用操作符:in / not in(在指定的集合范围内多选一 / 不在指定的集合范围内) |
| 行子查询 | 子查询结果为一行,但可以是多列,常用操作符:= 、<> 、IN 、NOT IN |
| 表子查询 | 子查询结果为多行多列[相当于子查询结果是一张表], |
子查询可书写的位置为:where之后、from之后、select之后。
**要点:**先对需求做拆分,明确具体的步骤,然后再逐条编写SQL语句。 最终将多条SQL语句合并为一条。
三、分页条件查询
1、使用PageHelper进行分页查询
①SQL语句查询
使用LIMIT关键字,格式为:limit 开始索引 每页显示的条数。
sql
select * from emp limit 0,10;
-- 查询第1-10条数据作为第一页
select * from emp limit 10,10;
-- 查询第11-20条数据作为第二页
②封装页数和记录数
后台给前端返回的数据包含:List集合(数据列表)、total(总记录数)。而这两部分我们通常封装到PageResult对象中,并将该对象转换为json格式的数据响应回给浏览器。并定义对应的实体类用于条件查询
java
@Data
@NoArgsConstructor
@AllArgsConstructor
public class PageResult {
private Long total; //总记录数
private List rows; //当前页数据列表
}
java
@Data
public class EmpQueryParam {
private Integer page;
private Integer pageSize;
private String name;
private Integer gender;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate begin;
@DateTimeFormat(pattern = "yyyy-MM-dd")
private LocalDate end;
}
③在maven中导入依赖
XML
<!--分页插件PageHelper-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.7</version>
</dependency>
④Controller层
java
@GetMapping
public Result page(EmpQueryParam empQueryParam)
{
log.info("查询员工信息,{}",empQueryParam);
PageResult pageResult = empService.page(empQueryParam);
return Result.success(pageResult);
}
⑤在ServiceImpl中重写page方法
Service:
java
/**
* 分页查询
*/
PageResult page(EmpQueryParam empQueryParam);
ServiceImpl:
java
@Override
public PageResult page(Integer page, Integer pageSize) {
//1. 设置分页参数
PageHelper.startPage(page,pageSize);
//2. 执行查询
List<Emp> empList = empMapper.list();
Page<Emp> p = (Page<Emp>) empList;
//3. 封装结果
return new PageResult(p.getTotal(), p.getResult());
}
⑥编写Mapper层和对应的xml
java
/**
* 分页查询员工信息
*/
public List<Emp> list(EmpQueryParam empQueryParam);
XML
<select id="list" resultType="com.genshin.pojo.Emp">
select e.*, d.name deptName from emp e left join dept d on e.dept_id = d.id
<where>
<if test="name != null and name != ''">
e.name like concat('%',#{name},'%')
</if>
<if test="gender != null">
and e.gender = #{gender}
</if>
<if test="begin != null and end != null">
and e.entry_date between #{begin} and #{end}
</if>
</where>
order by e.update_time desc
</select>
**注意:**PageHelper实现分页查询时,SQL语句的结尾一定一定一定不要加分号(;).。PageHelper只会对紧跟在其后的第一条SQL语句进行分页处理。