逻辑数据库设计 - 乱穿马路(多对多关系)

一、乱穿马路模式介绍

  程序员通常使用逗号分隔的列表来避免在多对多关系中创建交叉表,这种设计方式定义为一种反模式,称为乱穿马路。

  例如:在一个产品管理系统中,一个人可以有多个产品,一个产品必须对应一个人,因此有如下数据库:

  

  但是,随着时间的推移,出现了一个产品可能会有多个联系人。于是为了最小限度地修改数据库,可能不少人会将Account_Id的类型修改成varchar,这样就可以列出该列中的多个账号Id,每个Id之间用逗号分隔。这样的设计貌似可行,因为并没有创建额外的表或者列。仅仅改变了一个字段的数据类型就成功达到目的。以下我们来列举一下这样做的缺点。改变之后的数据库外键去掉,同时Product_Account变为varcahr。

  

  1、查询指定账号的产品。

  因为所有的外键都合并在一个单元格内,查询会变得相当困难。这时候不能在使用逗号。下面来测试下查询所有账号Id为1的产品。

  2、查询指定产品的账号

  使用逗号分隔的列表来做多表联结查询定位一行数据也是既不优雅和耗时的。

  3、执行聚合查询

  聚合查询使用SQL内置的聚合函数。如count(),sum(),avg()。然后这些函数是针对分组进行而设计的,并不是为了逗号分隔的列表,使用这些聚合函数也变得困难。

  4、更新指定产品的账号  

  你可以用字符串拼接的方式在列表尾端增加一个新的ID,但这并不能使列表按顺序存储。

  5、删除指定产品的账号

  6、验证产品ID

  如何防止用户在Product_Account列中输入诸如‘apple‘这样的非法字段。

  7、选择合适的分隔符

  如果存储一个字符串列表,而不是数字列表,列表中的某些条目可能会包含分隔符。使用逗号作为分隔符可能会有问题。当然,到时候可以再换一个字符,但是即使这样也无法确保这个新字符永远不会出现的条目中,

  8、列表长度限制

  你能在一个varchar(50)的结构中存多少数据呢?这依赖于每个条目的长度,如果每个条目只有2个字符长,那你能够存10多条数据,但如果每个条目长度为6,你就只能存几个了。

  如果你的团队中说过下面这些话,那么很有可能就是在项目中使用了“乱穿马路”的设计模式的线索了。

  (1)列表最多支持存放多少条数据?

    这个问题在选择varchar列的最大长度时被提及,因为长度不好确定

  (2)你知道在SQL中如何做分词查找吗?

    如果你用了正则表达式来提取字符串中的组成部分,这可能是一种提示,意味着你应该把这些数据分开存储。

  (3)哪些字符不会出现在任何一个列表条目中?

    你想要使用一个不会令人困惑的分割符号。但任何字符都有可能在某天出现在字段中的某个值内。

二、合理使用反模式

  如果你的应用程序需要逗号分隔的这种存储格式,也可能没必要获取列表中的单独项。同样,如果应用程序接收的源数据时有逗号分隔的格式,而你只需要存储和使用它们并且不对其做任何修改,你完全不必要分开其中的值。

三、终极解决方案 - 创建一张交叉表

  将Account_Id存储在一张单独的表中,而不是存储在Product表中,从而每个Account都可以占据一行。这张新表称为Contacts。实现了Product与Account的多对多关系。

  设计后的数据库关系图如下所示:

  

  剩下的就不说了,你会发现,如果是基于以上3张表的设计,那么对于以上乱穿马路模式提出的8个问题都能够轻松解决。另外,为Contacts.Account_Id做索引的查询效率比用逗号分隔列表中分串要高效得多。在许多数据库当中,声明某一列为外键都会隐式地为该列创建索引。

  另外,你还可以再交叉表中添加其他属性,比如,可以记录一个联系人被加入产品的具体日期,或产品的第一联系人及第二联系人。这些都是在逗号分隔的列表中无法做到的。

时间: 2024-08-29 04:06:11

逻辑数据库设计 - 乱穿马路(多对多关系)的相关文章

逻辑数据库设计 - 需要ID(谈主键Id)

本文的目标就是要确认那些使用了主键,却混淆了主键的本质而造成的一种反模式. 一.确立主键规范 每个了解数据库设计的人都知道,主键对于一张表来说是一个很重要,甚至必需的部分.这确实是事实,主键是好的数据库设计的一部分.主键是数据库确保数据行在整张表唯一性的保障.它是定位到一条记录并且确保不会重复存储的逻辑机制.主键也同时可以被外键引用来建立表与表之间的关系. 难点是选择那一列作为主键.大多数表中的每个属性值都有可能被很多行使用.例如姓名,电子邮件地址等等都不能保证不会重复. 在这样的表中,需要引入

SQL反模式学习笔记2 乱穿马路

2014-10-10 14:12:02 程序员通常使用逗号分隔的列表来避免在多对多的关系中创建交叉表, 将这种设计方式定义为一种反模式,称为“乱穿马路”. 目标:  存储多属性值,即多对一 反模式:将多个值以格式化的逗号分隔存储在一个字段中          比如:ProductAccount表(Contacts表),产品与账号信息表,一个产品有有多个联系人账号信息. 1.查询:查询指定账号的产品.不能使用SQL语法中的等号操作符,只能使用like 或者正则表达式,索引将不可用,查询效率降低.

逻辑数据库设计 - 单纯的树(递归关系数据)(转)

逻辑数据库设计 - 单纯的树(递归关系数据) 相信有过开发经验的朋友都曾碰到过这样一个需求.假设你正在为一个新闻网站开发一个评论功能,读者可以评论原文甚至相互回复. 这个需求并不简单,相互回复会导致无限多的分支,无限多的祖先-后代关系.这是一种典型的递归关系数据. 对于这个问题,以下给出几个解决方案,各位客观可斟酌后选择. 一.邻接表:依赖父节点 邻接表的方案如下(仅仅说明问题): CREATE TABLE Comments( CommentId int PK, ParentId int, --

数据库设计之E-R模型转换成关系模型

个人重构版机房收费系统中需要自己重新设计数据库,那么如何设计数据库呢?这也是咱们自考中一门重要的课程<数据库原理>,对于考过这科的同学想必已经从中受益,直接就可以学以致用.我是今年10月份考这本书,所以就先实践一把. 原先看过的耿建玲老师的视频,里面重点讲解的是数据库内部的具体操作,没有怎么涉及到数据库设计中不可或缺的一个步骤即从概念模型到逻辑模型的转换.进入正题: 背景:数据系统生存期 我们把数据库应用系统从开始规划.设计.实现.维护到最后被新的系统取代而停止使用的整个期间,称为数据库系统生

数据库设计中一对一、多对一、多对多关系依据外键的实现条件及方法

作者:二歪求知iSk2y链接:https://www.jianshu.com/p/2b27c7ba0653来源:简书 下面以departments和staff_info表为例(为staff_info添加指向departments的外键) 一个表的字段作为外键的条件: 列值必须非空且唯一 测试例子如下: mysql> create table departments (dep_id int(4),dep_name varchar(11)); Query OK, 0 rows affected (0

逻辑数据库设计 - 无视约束(谈外键)

有一些开发人员不推荐使用完整性约束,你可能听过以下这么几点不使用外键的原因. 1.数据更新有可能和约束冲突. 2.当前的数据库设计如此灵活,以致于不支持引用完整性约束. 3.数据库为外键建立的索引会影响性能. 4.当前使用的数据库不支持外键. 5.定义外键的语法并不简单,还需要查阅. 一.反模式:无视约束 即使第一感觉告诉你,省略外键约束能使得数据库设计更加简单.灵活,或者执行更加高效,你还是不得不在其他方面付出相应的代价 -- 必须增加额外的代码来手动维护引用完整性. 1.完整性问题 很多人对

逻辑数据库设计 - 可变属性(继承)

可变属性的需求:我们需要在数据库里面存储很多电器,比如电视,冰箱等等.通常,在程序中,我们的类图为: EVA设计 对于这种继承下来的可变属性时,有一种办法是创建另外一张表,将属性当成行来存储. 其中存储的数据类似下面这样: 这样的设计称为:实体-属-值,简称:EVA,或者又叫开放架构.无模式. 这种设计有如下3种好处: 1.这两张表的列都很少. 2.新增的属性不会对现有的表结构造成影响,不需要新增列. 3.避免由于空值而造成的表内容混乱. 但是这样也有如下缺点: 1.查询属性 本来,我们想要按出

逻辑数据库设计 - 单纯的树(递归关系数据)

相信有过开发经验的朋友都曾碰到过这样一个需求.假设你正在为一个新闻网站开发一个评论功能,读者可以评论原文甚至相互回复. 这个需求并不简单,相互回复会导致无限多的分支,无限多的祖先-后代关系.这是一种典型的递归关系数据. 对于这个问题,以下给出几个解决方案,各位客观可斟酌后选择. 一.邻接表:依赖父节点 邻接表的方案如下(仅仅说明问题): CREATE TABLE Comments( CommentId int PK, ParentId int, --记录父节点 ArticleId int, Co

逻辑数据库设计 - 元数据分裂(分区表)

我之前曾参与维护过一个舆情监控系统,该系统每天源源不断地监控着互联网上的新闻,不断从网上下载新闻保存进入数据库. 提出问题 为了表述简单,我特意模拟了一张类似的表: CREATE TABLE NEWS( Id int PK, Title nvarchar(500) --新闻标题 Content text --新闻内容 CreateTime DateTime) 随着时间的推移,数据库里的新闻变得越来越多,系统开始跑得越来越慢.随后,技术经理考虑到,舆情监控需要的仅仅是近期的数据,过时的数据,不太重