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