[不懂就问] 两个多对多关联该如何优雅查询?
数据库结构如下:
rosters #排班表
| id | name 备注 | date 上班日期 | … |
|---|---|---|---|
| 1 | 加班 | 2020-11-20 | … |
| 2 | 正常 | 2020-11-19 | … |
| N | … | … | … |
shifts #班次表
| id | name 上班时间 |
|---|---|
| 1 | 08点-12 点 |
| 2 | 13点-18 点 |
| 3 | 19点-23 点 |
| 4 | 00点-07 点 |
| N | … |
staffs #员工表
| id | name 姓名 | … |
|---|---|---|
| 1 | 小明 | … |
| 2 | 小东 | … |
| 3 | 小红 | … |
| 4 | 小月 | … |
| N | … | … |
roster_shift #排班_班次_中间表
| id | roster_id 排班 ID | shift_id班次 ID | 注释 |
|---|---|---|---|
| 1 | 1 | 1 | 班次 1 |
| 2 | 1 | 2 | 班次 2 |
| 3 | 1 | 3 | 班次 3 |
| 4 | 1 | 4 | 班次 4 |
| 5 | 2 | 1 | 班次 1 |
| N | … | … | … |
shift_staff #班次_员工_中间表
| id | shfit_id 班次ID | staff_id 员工ID | 注释 |
|---|---|---|---|
| 1 | 1 | 1 | 小明 |
| 2 | 1 | 2 | 小东 |
| 3 | 2 | 2 | 小东 |
| 4 | 3 | 3 | 小红 |
| 5 | 4 | 4 | 小月 |
| 6 | 1 | 1 | 小明 |
| N | … | … |
结构图

问题回溯
根据上面表格列出的数据,查询关联的班次再查询关联的员工
$rosters = Roster::with('shifts.staffs')->get();
查询结果如下:
Array
(
[0] => Array
(
[id] => 1
[name] => 加班
[date] => 2020-11-20
[created_at] => 2020-11-22T12:34:09.000000Z
[updated_at] => 2020-11-22T12:34:09.000000Z
[shifts] => Array
(
[0] => Array
(
[id] => 1
[name] => 08点-12点
[pivot] => Array
(
[roster_id] => 1
[shift_id] => 1
)
[staffs] => Array
(
[0] => Array
(
[id] => 1
[name] => 小明
[status] => 0
[pivot] => Array
(
[shift_id] => 1
[staff_id] => 1
)
)
[1] => Array
(
[id] => 2
[name] => 小东
[status] => 0
[pivot] => Array
(
[shift_id] => 1
[staff_id] => 2
)
)
)
)
[1] => Array
(
[id] => 2
[name] => 13点-18点
[pivot] => Array
(
[roster_id] => 1
[shift_id] => 2
)
[staffs] => Array
(
[0] => Array
(
[id] => 2
[name] => 小东
[status] => 0
[pivot] => Array
(
[shift_id] => 2
[staff_id] => 2
)
)
)
)
[2] => Array
(
[id] => 3
[name] => 19点-23点
[pivot] => Array
(
[roster_id] => 1
[shift_id] => 3
)
[staffs] => Array
(
[0] => Array
(
[id] => 3
[name] => 小红
[status] => 0
[pivot] => Array
(
[shift_id] => 3
[staff_id] => 3
)
)
)
)
[3] => Array
(
[id] => 4
[name] => 00点-07点
[pivot] => Array
(
[roster_id] => 1
[shift_id] => 4
)
[staffs] => Array
(
[0] => Array
(
[id] => 4
[name] => 小月
[status] => 0
[pivot] => Array
(
[shift_id] => 4
[staff_id] => 4
)
)
)
)
)
)
[1] => Array
(
[id] => 2
[name] => 正常
[date] => 2020-11-20
[created_at] => 2020-11-22T12:34:09.000000Z
[updated_at] => 2020-11-22T12:34:09.000000Z
[shifts] => Array
(
[0] => Array
(
[id] => 1
[name] => 08点-12点
[pivot] => Array
(
[roster_id] => 2
[shift_id] => 1
)
[staffs] => Array
(
[0] => Array
(
[id] => 1
[name] => 小明
[status] => 0
[pivot] => Array
(
[shift_id] => 1
[staff_id] => 1
)
)
[1] => Array
(
[id] => 2
[name] => 小东
[status] => 0
[pivot] => Array
(
[shift_id] => 1
[staff_id] => 2
)
)
)
)
)
)
)
问题总结:
通过数据库的数据对比查询结果可以看出
$rosters[1]也就是2020-11-19排班日,这条数据正确结果应该是在2020-11-19 08点-12点这个班次 只有小明一个员工才对,但是由于目前的数据表关联关系,导致查出来多了个小东,小东实际上是属于第一条的数据,也就是 排班表 ID 为 1 的那条数据。
错误结果如下:
| 排班日期 | 班次 | 员工 |
|---|---|---|
| 2020-11-20 | 08点-12点 | 小明,小东 |
| 13点-18点 | 小东 | |
| 19点-23点 | 小红 | |
| 00点-07点 | 小月 | |
| 2020-11-19 | 08点-12点 | 小明,小东 |
期望结果如下:
| 排班日期 | 班次 | 员工 |
|---|---|---|
| 2020-11-20 | 08点-12点 | 小明,小东 |
| 13点-18点 | 小动 | |
| 19点-23点 | 小红 | |
| 00点-07点 | 小月 | |
| 2020-11-19 | 08点-12点 | 小明 |
已做尝试
我自己尝试过在
shift_staff中间表加 1 个roster_id字段,用来关联,但是这样子的话,所有的CURD操作每次都要写很多foreach和条件来查询对应的员工,感觉非常的不优雅,特别是如果不使用DB只用Eloquent的方式的话,更加不好弄,当然可能是没经验想不出更好的办法。
寻求解决
上面列举的只是 2 条数据的情况下,如果数据再多一点的会导致
班次和员工的关联数据全部错误,目前的疑问是不知道如何改进,到底是我们数据表关联的有问题,还是建表就有问题。或是说查询方式不对呢。新手刚接触 laravel不久虚心受教,最好能贴代码参考。
关于 LearnKu
粗略看了一下
rosters #排班表#与shifts #班次表的关系应该是一对多而不是多对多,也就是说shifts增加一个roster_id就可以解决@Jiangqh 感谢回复,若是在
shifts 表增加 roster_id确实可以解决问题,但如果rosters 和 shifts是一对多的话,班次就不能复用,每天的排班都要增加班次,所以采用了多对多,只要班次一样可以复用班次,也可以随意组合班次,并且如果修改了某一班次数据,关联的数据也会同步更新。实际的业务中这个只是其中一小块,只是为了方便提问单独拎出来提问。现在的问题是假设一定要用多对多的情况下,或者也不叫多对多吧,就是如果想要在能复用班次的情况下,如何解决问题呢?你这shift_staff表有问题啊,第一条和第六条数据是一样的,所以导致查询结果错误,想保证查询语句不动的话那你的第六条的shfit_id 班次 ID就不应该是1,而应该是一个新的08 点 - 12 点班次 ID, 比如5?就是说要保证shfit_id 班次 ID和staff_id 员工 ID的组合是唯一的
接楼上,班次和员工的关联关系不对,需要的关联是某天排班的某个班次的某些员工,现在的关联是班次关联到了员工,跟排班没有关联
@Klightsaber 确实是这样导致的查询结果错误,但是因为 roster表想和shift表做多对多关联,就导致ID重复 是因为复用了,如果想保留
roster表想和shift表多对多关系下,有没好的方法。表的设计有问题
shift_staff表里面的shfit_id修改为roster_shift的id,因为
班次与排班是多对多的关系,他们组合起来才是一个整体,才是你要关联的内容,只关联单独一个会出现很多脏数据。不过我觉得可以考虑的更简单一点,直接删除
shift_staff这个表,在roster_shift表增加一个user_id更简单一些。综上,两种解决方式,我更倾向于后一种删一个表。
加个
group表,把shift_staff拆分成staff_group和group_staff。这样就可以实现每天有多种班次,每个班次可以关联多个考勤组,再把人和考勤组关联。
按题目给的数据来看,小明和小东分属于两个组
如果我来写这个程序只需要三个表 排班表 员工表 多对多关系表