[不懂就问] 两个多对多关联该如何优雅查询?

数据库结构如下:

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不久虚心受教,最好能贴代码参考。

《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 8

粗略看了一下 rosters #排班表#shifts #班次表 的关系应该是 一对多 而不是 多对多,也就是说 shifts增加一个 roster_id 就可以解决

3年前 评论

@Jiangqh 感谢回复,若是在shifts 表增加 roster_id确实可以解决问题,但如果 rosters 和 shifts 是一对多的话,班次就不能复用,每天的排班都要增加班次,所以采用了多对多,只要班次一样可以复用班次,也可以随意组合班次,并且如果修改了某一班次数据,关联的数据也会同步更新。实际的业务中这个只是其中一小块,只是为了方便提问单独拎出来提问。现在的问题是假设一定要用多对多的情况下,或者也不叫多对多吧,就是如果想要在能复用班次的情况下,如何解决问题呢?

3年前 评论

你这shift_staff表有问题啊,第一条和第六条数据是一样的,所以导致查询结果错误,想保证查询语句不动的话那你的第六条的shfit_id 班次 ID就不应该是1,而应该是一个新的08 点 - 12 点班次 ID, 比如5?就是说要保证shfit_id 班次 ID和staff_id 员工 ID的组合是唯一的

3年前 评论

接楼上,班次和员工的关联关系不对,需要的关联是某天排班的某个班次的某些员工,现在的关联是班次关联到了员工,跟排班没有关联

3年前 评论

@Klightsaber 确实是这样导致的查询结果错误,但是因为 roster表想和shift表做多对多关联,就导致ID重复 是因为复用了,如果想保留roster表想和shift表多对多关系下,有没好的方法。

3年前 评论
Klightsaber 3年前

表的设计有问题

shift_staff 表里面的 shfit_id修改为 roster_shiftid

因为班次排班多对多的关系,他们组合起来才是一个整体,才是你要关联的内容,只关联单独一个会出现很多脏数据。

不过我觉得可以考虑的更简单一点,直接删除 shift_staff 这个表,在roster_shift表增加一个 user_id 更简单一些。

综上,两种解决方式,我更倾向于后一种删一个表。

3年前 评论
秦晓武

加个group表,把shift_staff拆分成staff_groupgroup_staff

这样就可以实现每天有多种班次,每个班次可以关联多个考勤组,再把人和考勤组关联。

按题目给的数据来看,小明和小东分属于两个组

3年前 评论

如果我来写这个程序只需要三个表 排班表 员工表 多对多关系表

3年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!