项目里有几个多对多关系,需要表达到关系数据库。
比如:用户 角色 权限; 用户与角色是多对多关系;角色与权限是多对多关系;
一般人会分别创建 [用户角色表]、[角色权限表],来存储多对多关系,这个没啥好说的。
现在队伍里一个小伙子嫌麻烦,就创建了一张 [数据关系表],大致字段有:[第一个 id],[第二个 id],[表名]。
大家分析下,这种做法,要如何评价

项目里有几个多对多关系,需要表达到关系数据库。
比如:用户 角色 权限; 用户与角色是多对多关系;角色与权限是多对多关系;
一般人会分别创建 [用户角色表]、[角色权限表],来存储多对多关系,这个没啥好说的。
现在队伍里一个小伙子嫌麻烦,就创建了一张 [数据关系表],大致字段有:[第一个 id],[第二个 id],[表名]。
大家分析下,这种做法,要如何评价
1 ScotGu Aug 27, 2021 如果一张表就能搞定这个项目,那岂不是应该献上膝盖? |
2 securityCoding Aug 27, 2021 瞎搞 |
3 heyjei Aug 27, 2021 多对多的多态关系,如果他能够在 ORM 里面把这种关系配出来,就这样呗。配不出来就老老实实的多建几张表 |
4 zjj19950716 Aug 27, 2021 让他弄呗 看能玩出什么花样 |
5 buliugu Aug 27, 2021 可以命名成数据爆炸表(逃 |
6 sy20030260 Aug 27, 2021 这种是典型的反规范建模设计,本身是没错的,得看具体的业务场景。 但是 [用户 角色 权限] 这种系统设计,本来就是典型的规范建模,具体到数据库设计又变成反规范的,就有点奇怪了~ |
7 levelworm Aug 27, 2021 via Android 我擦表中表啊! |
8 cadl Aug 27, 2021 最终这个模块变成了个图数据系统 XD |
9 bk201 Aug 27, 2021 就是将关系类的数据统一放在一个表里了,数据量不大的情况下感觉也没什么问题 |
10 yeeli Aug 27, 2021 数据不大都无所谓, 如果你以后是要搽屁股的负责人就提早改,很多时候项目和员工都撑不到爆的那一天 |
11 shanghai1943 Aug 27, 2021 一表多用了。不合理的设计。 |
12 rabbbit Aug 27, 2021 没太看懂,是指他存了这么个表吗? id1 | id2 | 表名 角色 id1| 权限 id1 | 用户权限表名 角色 id1| 权限 id2 | 用户权限表名 角色 id2| 权限 id1 | 用户权限表名 权限 id1| 角色 id1 | 用户角色表名 |
13 k9982874 Aug 27, 2021 直接甩他直属领导脸上,问就你招的这么个玩愣? |
14 l00t Aug 27, 2021 不知道你在说什么东西,什么第一个 ID 第二个 ID 表名,人家的字段没实际业务含义的吗? |
15 wolfie Aug 27, 2021 又是当时没怼过,越想越气。 user_role 、role_operation 这类设计较通用的表强行复杂化。 增加他人理解成本,并没有降低什么工作量,大数据量速度还差。 |
17 cocong Aug 27, 2021 不是你负责的就不用管,你也管不了,瞎操心。 如果出问题了你要跟着背锅的,那就把问题反映给上级,把锅甩出去。 总的来说,如果数据量小,版本迭代慢,那怎么搞都问题。如果数据量大,或者需求经常变动,那后面加班只会越来越多。 |
18 fgwmlhdkkkw Aug 27, 2021 我觉得可以优化一下,表名改为数字,挺好的吧…… |
20 comoyi Aug 27, 2021 5 张表,用户,角色,用户-角色关系,权限,角色-权限关系 |
21 qping Aug 27, 2021 貌似用着也没有什么问题,数据量一般也不会大 就是感觉怪怪的能用但是不够漂亮。 |
22 young1lin Aug 27, 2021 via Android 反范式设计,开发一时爽,维护火葬场。除非这个项目一定是他维护,不然让他改 |
23 young1lin Aug 27, 2021 via Android 这个就是典型的 RBAC0 设计思路的表,你可以把更详细的 RBAC1, RBACA2, RBAC3 讲给他,还有 ACL,ABAC, PBAC 讲给他,让他去查。还有 Shiro/Spring Security 怎么实现的,最好是 Shiro 比较简单。再对比下他的设计,再让他说说你懂不懂。 |
24 EPr2hh6LADQWqRVH Aug 27, 2021 你不了解,就别瞎说 邻接表,这是图查询的基石,只要数据库稍微支持一下图查询,这个结构能提供的比你那个捉鸡所谓关系模型多得多 |
25 soulzz Aug 27, 2021 那还不如全用 mongodb 用户权限也和用户基本资料放一起,直接定义各个接口的权限 |
26 R18 Aug 27, 2021 你让他严格按照 BCNF 来就行了。 |
27 GG668v26Fd55CP5W Aug 27, 2021 via iPhone 楼主这表达能力让我想了半天,什么 id1,id2 的,说服力估计也不行 |
28 jtwor Aug 27, 2021 数据库三范式。。。 那这样关联岂不是要带上 表名 条件。。 |
29 YUyu101 Aug 27, 2021 小伙子这样做就代表他把这些关系当成一个实体了,如果这些表的关系真的只有关系那没什么好说的,万一以后要给每种关系加各自属性,那他是准备搞父子表还是让多出来的字段空着,其实怎么设计都行,只要能评估好未来的工作量。 |
30 johnsonqrr Aug 27, 2021 还能怎么办,上网发个贴呗 |
31 mosakashaka Aug 27, 2021 索引搞好效率不会有问题。 |
32 Frauhling Aug 27, 2021 @fgwmlhdkkkw 这个表名改为数字的主意听着就很好 XD 就是不知道是楼主先跑路还是这个小伙子先跑路 |
33 zxcslove Aug 27, 2021 这样最大的问题是,不同的关联关系如果有不同的数据就悲剧了,只是做关联用你是杠不过他的。 |
34 sivl6p OP @rabbbit 是这样存的 真实建表语句如下: CREATE TABLE [dbo].[DataConnect]( --数据关系表,可存一切多对多关系 [Id] [uniqueidentifier] NOT NULL, [SourceCode] [nvarchar](100) NULL, --存表名 [FirstId] [uniqueidentifier] NULL, --第一个 id [SecondId] [uniqueidentifier] NULL, --第二个 id [ThirdId] [uniqueidentifier] NULL, --预留字段 ... 其它预留字段 |
37 sivl6p OP 12 楼的最后一行不准确,我修改下 @DavidDee id1 | id2 | 表名 角色 id1| 权限 id1 | 用户权限表名 角色 id1| 权限 id2 | 用户权限表名 角色 id2| 权限 id1 | 用户权限表名 用户 id1| 角色 id1 | 用户角色表名 用户 id1| 角色 id2 | 用户角色表名 |
38 code4you Aug 27, 2021 只要小伙子 能搞定 能跑就行 |
39 baoshijiagong Aug 27, 2021 sql 里面,表名如果写错,不会报错,还会以为没有数据。不好排查原因。 |
40 DavidDee Aug 27, 2021 @5sheep 所以就是他将本来该分为两个多对多的关系表,直接使用了一个表设计,所以 id1,id2 是字段是无法确定存放的是什么内容。 如果表名 == 用户角色表名,那么 id1 就是用户 id,id2 就是角色 id ; 如果表名 == 用户权限表名,那么 id1 就是角色 id,id2 就是权限 id 。 |
41 pws22 Aug 27, 2021 说实话,如果没有一些关联特殊要求,其实设计一个大表做关联没什么毛病,就是直观看上去不好理解,但是程序逻辑能弄清就行,但是如果关联上有一些额外的字段,说不定过了一段时间再看,都会忘记了当初的设计条件了,看代码也会一脸懵,咋回事,都是反复调用这个大关联表来做操作..不能图一时轻松,还有考虑后期的维护成本和其他后来人的接受程度,选择一个大家都能比较好理解的做法. ps 如果是我组成员做出这样的,限期修改吧. |
42 murmur Aug 27, 2021 好家伙,表名直接写到数据里么 |
43 c6h6benzene Aug 27, 2021 via iPhone 人和程序有一个能跑就行 /doge |
44 baoshijiagong Aug 27, 2021 |
45 baoshijiagong Aug 27, 2021 有这种表的存在,连数据库表的模型图都不能直观画出来。 |
46 kingfalse Aug 27, 2021 这哥们把 mysql 当 mongo 用了吗这是 |
47 vone Aug 27, 2021 笑死,那可以把所有表都合并成一个,极为方便。 |
48 way2create Aug 27, 2021 感觉这样更复杂啊其实 |
49 way2create Aug 27, 2021 感觉你脸皮没他厚 怼不过他 |
50 rationa1cuzz Aug 27, 2021 我接受这个项目,小伙是这么建表的:[角色权限 1 表]、[角色权限 2 表]、[角色权限 3 表]、[角色权限 4 表]。。。。 |
51 cnrting Aug 27, 2021 via iPhone 这不是你该操心的事 |
52 ttyn Aug 27, 2021 公司流程问题,这样的设计也并非不可以,要结合具体的项目来看, 稍微正规点的应该有个“设计 -> 评审 ”流程 评审通过,出问题大家一起背锅, 评审不通过,打回去修改 |
53 fml87 Aug 27, 2021 典型的眼高手低,场景又不复杂,这样搞只是白白增加维护成本 |
54 intmax2147483647 Aug 27, 2021 你不了解,就别瞎说。 |
55 11232as Aug 27, 2021 A 多对多 B,B 多对多 C | B_id | A/C id | A/C 表名| | --- | --- | --- | | ... | ... | ... | 虽然有点怪但也没啥问题... |
56 cszchen Aug 27, 2021 via iPhone 没看出来哪儿方便了,工作量不仅不会变小,可能还会变大,如果有可能就让他做简单的 curd,或者直接开掉 |
57 redford42 Aug 27, 2021 表设计还是评审一下吧 |
58 JoeBreeze Aug 27, 2021 团队合作还是需要一些规范的, 他喜欢这样做, 然后其他人也大部分都认同, 打不过就加入吧, 后面有锅给他背就好了 |
59 egfegdfr Aug 27, 2021 这么搞,在做删除、新增操作的时候,不容易产生死锁吗? 虽能说 可以通过加索引、或者是分布式锁控制,但是这个设计好几个业务逻辑,感觉这么设计的优点是小于缺点的 |
60 Carlgao Aug 27, 2021 在多对多中间表里面加表名类型区分,可以减少中间表的数量,这属于常规操作吧。 |
61 drinke9 Aug 27, 2021 这种设计模式,laravel 框架中的一个权限包就是这种结构,`laravel-permission` |
62 timethinker Aug 27, 2021 看场景吧,一般角色对权限的是配置数据,数据量不大可以做缓存,更新的情况也比较少。甚至可以把角色对权限的这个关系直接通过配置文件存储起来,都不用存数据库。 而用户对角色的这部分数据是动态数据表,适合放到数据库中。话说回来,量变导致质变,不同的量级有不同的做法,条条大路通罗马。有规范的按照规范执行,没有的话按照简单的来做就行了,在没有规范的前提下就不要苛求太多。 |
63 0o0o0o0 Aug 27, 2021 关键是这逻辑是怎么想出来的,正常人第一次设计都会去网上搜索一下经验或者规范吧,所以说这是自己发明的神奇设计方法吗哈哈哈 |
64 charseer Aug 27, 2021 一些参数表这样设计没啥问题,很好用的 |
65 Muyiafan Aug 27, 2021 表中表中表中表中表中表 |
66 superrichman Aug 27, 2021 @baoshijiagong 怕模型图最后画出来是个刺猬 /doge |
67 Guidoo Aug 27, 2021 好家伙 套娃 表中表中表中表中表中表中表中表中 |
68 aliveyang Aug 27, 2021 他以为动态了,其实是乱搞 |
69 Leviathann Aug 27, 2021 via iPhone 元编程是吧 |
70 conanforever22 Aug 27, 2021 他可能是觉得项目中有很多这种“类似”的关系, 不想建那么多表 感觉像我们平时分析某个业务场景, 经过思考,抽象出了一个精炼“通用”的结构;可能真的通用,能应对未来各种变化;也可能忽视了这些变化,然后各种 if...else...特殊处理 看表结构里他还建了很多预留字段,可能是考虑到某些关系可能会附加一些属性,如果能解决问题也没有性能问题,其他同事也能看懂没意见,管他呢... |
71 guisheng Aug 27, 2021 via iPhone 个人觉得,这种做法在解决问题上是没有什么好说的。但是在可维护性上和可扩展性上 直接一巴掌拍死。 |
72 guanhui07 Aug 27, 2021 via Android 不合理 |
73 pepesii Aug 27, 2021 这有点像 CASBIN 的 RBAC 模型呢 |
74 twing37 Aug 27, 2021 第一感觉是 casbin 的模型 |
75 konakona Aug 27, 2021 这个表中表的设计必须是 1:1 的关系就是合理可用且没有诟病的,我指的是: Aid | Bid | model | remark 1 | 2 | string | ... 比如 Aid 的 field name 可以是 permission_id,Bid 的 field name 可以是 model_id 或者 role_id,model 的 field name 可以是某种 type (比如 model_type 或者任何能够代表操作类的 field name 去说明它) 这个表不能复杂,最多就这么 3-4 个主要子段,因为它的定位应该是一个“中间表”。但……这也只是中间表,如果要在一个表里完成复杂的蛛网一般的交织,后续维护和理解成本(主要体现在陈长的代码里)会比较麻烦,不易维护和改造。 |
76 konakona Aug 27, 2021 我看了看楼主的 append,这个小伙的做法好像没什么问题啊- - 很常规,很 ok 。建议看下 casbin 。。。 |
77 ericls Aug 27, 2021 via iPhone GenericForeignKey |
78 temp178 Aug 27, 2021 via iPhone 感觉是建立了一个表达通用关系的表,如果是的话,可能也是很合适的 |
79 Casbin PRO 这是 Casbin |
81 mmdsun Aug 27, 2021 还有表名称? 关系数据库 Mysql 这种要怎么查询 ? |
82 seakingii Aug 27, 2021 反直觉 不过就这个场景来说,是能满足要求的,也能扩展应用到所有这种简单多对多的其它表.数据量不大的话性能也不是问题 |
83 akira Aug 27, 2021 这样的优点是 少创建一个表,嗯。 应该就这个了吧 |
84 luckyc Aug 28, 2021 还行啊, 没啥毛病. 我更倾向于把所有配置全写一个表里面去. ``` CREATE TABLE `system_config` ( `id` mediumint UNSIGNED NOT NULL AUTO_INCREMENT, `userid` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `module` varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, `section` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `key` char(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT '', `value` text CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, PRIMARY KEY (`id`) USING BTREE, UNIQUE INDEX `unique`(`userid`, `module`, `section`, `key`) USING BTREE ) ENGINE = MyISAM AUTO_INCREMENT = 2680 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; ``` |
85 cp19890714 Aug 28, 2021 via Android 谁最终为该系统负责,那就听谁的。当然,还是要以理服人,举例说明该方式在未来的隐患。 |
86 WilliamYang Aug 28, 2021 从业 6 年了,某些人总能找到说服你和他自己的理由。你不是他上级,能听就听,不听就算了 |
87 chih758 Aug 28, 2021 不是本科毕业的?数据库的范式没学? |
88 entro Aug 28, 2021 真实,本公司项目某模块的表就是老板亲自用这种方式设计的#(笑) |
89 hst001 Aug 28, 2021 via Android 数据量稍微多点,更新的时候就知道厉害了,后面让他再去拆表,长长教训 |
90 2i2Re2PLMaDnghL Aug 28, 2021 重新发明图数据库 把不同表(实际上就是不同的命名空间)的 id 放在一起,需要保证表间 id 仍然唯一(实质达成命名空间合并)。 这种情况下 MySQL 性能可能捉鸡 |
91 TimPeake Aug 28, 2021 套娃? |
92 wqtacc Aug 28, 2021 看起来是把角色权限和用户角色写到了一个表里?那描述应该是下面这样子? id1 | id2 | 表名 角色 id1| 权限 id1 | 用户权限表名 角色权限? 角色 id1| 权限 id2 | 用户权限表名 角色权限? 角色 id2| 权限 id1 | 用户权限表名 用户 id1| 角色 id1 | 用户角色表名 用户 id1| 角色 id2 | 用户角色表名 |
93 Biwood Aug 28, 2021 乍一看,还以为是什么数据库层面的高度抽象,专门存储表与表之间的关系?太高级了吧 看到“第一个 id”、“第二个 id”这里,有点懵逼了,这是用一个表解决整个系统的对应关系?有种用 0 和 1 写程序的感觉,按照这种写法,那数据库要“主键”、“外键”干嘛用 |
94 Biwood Aug 28, 2021 @avastms 我一开始也以为像是你说的邻接表,把数据之间的关系存储起来,但是你在细读一下楼主的描述跟他的回复,根本没那么高大上,他是反向操作,纯粹是把三张表之间两两排列组合,把每一条关系都存储在一个表里面。 打个比方,就像你本来可以用 for 循环打印 10 次数据,他偏偏不用 for 循环,而是硬写了 10 次打印数据的代码。 |
95 changdy Aug 29, 2021 |
96 encro Aug 29, 2021 没有问题, 其实 yii 框架的默认 rbac 差不多也是这么建立的。基于 NIST RBAC model,性能没有问题,且可以多级用户角色和权限继承。 DbManager 使用 4 个数据库表存放它的数据: itemTable: 该表存放授权条目(译者注:即角色和权限)。默认表名为 "auth_item" 。 itemChildTable: 该表存放授权条目的层次关系。默认表名为 "auth_item_child"。 assignmentTable: 该表存放授权条目对用户的指派情况。默认表名为 "auth_assignment"。 ruleTable: 该表存放规则。默认表名为 "auth_rule"。 有个要求就是需要封装很好。 |
97 encro Aug 29, 2021 下面是表结构,可以不考虑 rule 表 create table `auth_rule` ( `name` varchar(64) not null, `data` blob, `created_at` integer, `updated_at` integer, primary key (`name`) ) engine InnoDB; create table `auth_item` ( `name` varchar(64) not null, `type` smallint not null, `description` text, `rule_name` varchar(64), `data` blob, `created_at` integer, `updated_at` integer, primary key (`name`), foreign key (`rule_name`) references `auth_rule` (`name`) on delete set null on update cascade, key `type` (`type`) ) engine InnoDB; create table `auth_item_child` ( `parent` varchar(64) not null, `child` varchar(64) not null, primary key (`parent`, `child`), foreign key (`parent`) references `auth_item` (`name`) on delete cascade on update cascade, foreign key (`child`) references `auth_item` (`name`) on delete cascade on update cascade ) engine InnoDB; create table `auth_assignment` ( `item_name` varchar(64) not null, `user_id` varchar(64) not null, `created_at` integer, primary key (`item_name`, `user_id`), foreign key (`item_name`) references `auth_item` (`name`) on delete cascade on update cascade, key `auth_assignment_user_id_idx` (`user_id`) ) engine InnoDB; |
98 kiracyan Aug 30, 2021 其实就是把多个 map 表合在一起了吧 |