数据库表的设计方法和原则(转载)

在目前的企业信息系统中,数据库还是最佳的数据存储方式,虽然已经有很多的书籍在指导我们进行数据库设计,但应该那种方式是设计数据库的表结构的最好方法、设计时应遵从什么样的原则、四个范式如何能够用一种方式达到顺畅的应用等是我一直在思考和总结的问题,下文是我针对这几个问题根据自己的设计经历准备总结的一篇文章的提纲,欢迎大家一块进行探讨,集思广益。其中提到了领域建模的概念,但未作详细解释,希望以后能够有时间我们针对这个命题进行深入探讨。

1)不应该针对整个系统进行数据库设计,而应该根据系统架构中的组件划分,针对每个组件所处理的业务进行组件单元的数据库设计;不同组件间所对应的数据库表之间的关联应尽可能减少,如果不同组件间的表需要外键关联也尽量不要创建外键关联,而只是记录关联表的一个主键,确保组件对应的表之间的独立性,为系统或表结构的重构提供可能性。

2)采用领域模型驱动的方式和自顶向下的思路进行数据库设计,首先分析系统业务,根据职责定义对象。对象要符合封装的特性,确保与职责相关的数据项被定义在一个对象之内,这些数据项能够完整描述该职责,不会出现职责描述缺失。并且一个对象有且只有一项职责,如果一个对象要负责两个或两个以上的职责,应进行分拆。

3)根据建立的领域模型进行数据库表的映射,此时应参考数据库设计第二范式:一个表中的所有非关键字属性都依赖于整个关键字。关键字可以是一个属性,也可以是多个属性的集合,不论那种方式,都应确保关键字能够保证唯一性。在确定关键字时,应保证关键字不会参与业务且不会出现更新异常,这时,最优解决方案为采用一个自增数值型属性或一个随机字符串作为表的关键字。

4)由于第一点所述的领域模型驱动的方式设计数据库表结构,领域模型中的每一个对象只有一项职责,所以对象中的数据项不存在传递依赖,所以,这种思路的数据库表结构设计从一开始即满足第三范式:一个表应满足第二范式,且属性间不存在传递依赖。

5)同样,由于对象职责的单一性以及对象之间的关系反映的是业务逻辑之间的关系,所以在领域模型中的对象存在主对象和从对象之分,从对象是从1-N或N-N的角度进一步主对象的业务逻辑,所以从对象及对象关系映射为的表及表关联关系不存在删除和插入异常。

6)在映射后得出的数据库表结构中,应再根据第四范式进行进一步修改,确保不存在多值依赖。这时,应根据反向工程的思路反馈给领域模型。如果表结构中存在多值依赖,则证明领域模型中的对象具有至少两个以上的职责,应根据第一条进行设计修正。第四范式:一个表如果满足BCNF,不应存在多值依赖。

7)在经过分析后确认所有的表都满足二、三、四范式的情况下,表和表之间的关联尽量采用弱关联以便于对表字段和表结构的调整和重构。并且,我认为数据库中的表是用来持久化一个对象实例在特定时间及特定条件下的状态的,只是一个存储介质,所以,表和表之间也不应用强关联来表述业务(数据间的一致性),这一职责应由系统的逻辑层来保证,这种方式也确保了系统对于不正确数据(脏数据)的兼容性。当然,从整个系统的角度来说我们还是要尽最大努力确保系统不会产生脏数据,单从另一个角度来说,脏数据的产生在一定程度上也是不可避免的,我们也要保证系统对这种情况的容错性。这是一个折中的方案。

8)应针对所有表的主键和外键建立索引,有针对性的(针对一些大数据量和常用检索方式)建立组合属性的索引,提高检索效率。虽然建立索引会消耗部分系统资源,但比较起在检索时搜索整张表中的数据尤其时表中的数据量较大时所带来的性能影响,以及无索引时的排序操作所带来的性能影响,这种方式仍然是值得提倡的。

9)尽量少采用存储过程,目前已经有很多技术可以替代存储过程的功能如“对象/关系映射”等,将数据一致性的保证放在数据库中,无论对于版本控制、开发和部署、以及数据库的迁移都会带来很大的影响。但不可否认,存储过程具有性能上的优势,所以,当系统可使用的硬件不会得到提升而性能又是非常重要的质量属性时,可经过平衡考虑选用存储过程。

10)当处理表间的关联约束所付出的代价(常常是使用性上的代价)超过了保证不会出现修改、删除、更改异常所付出的代价,并且数据冗余也不是主要的问题时,表设计可以不符合四个范式。四个范式确保了不会出现异常,但也可能由此导致过于纯洁的设计,使得表结构难于使用,所以在设计时需要进行综合判断,但首先确保符合四个范式,然后再进行精化修正是刚刚进入数据库设计领域时可以采用的最好办法。

11)设计出的表要具有较好的使用性,主要体现在查询时是否需要关联多张表且还需使用复杂的SQL技巧。

12)设计出的表要尽可能减少数据冗余,确保数据的准确性,有效的控制冗余有助于提高数据库的性能

<think>我们正在设计一个数据库结构来支持“标记”功能。标记(Tagging)通常指为数据项附加一个或多个关键词(标签),以便于分类检索。根据需求,我们需要设计一个灵活且高效的结构来支持以下功能: 1. 一个数据项(例如文章、商品等)可以被标记多个标签。 2. 一个标签可以被用于多个数据项。 3. 能够方便地查询具有某个标签的所有数据项,以及一个数据项的所有标签。 这种多对多的关系通常需要三个: - 一个用于存储数据项(例如:文章、商品等)。 - 一个用于存储标签(标签)。 - 一个关联(中间)来建立数据项标签之间的多对多关系。 具体设计如下: 1. **数据(例如`articles`)**:存储需要被标记的数据项。 - 该至少包含一个主键(如`id`)其他业务字段(如标题、内容等)。 - 示例结构: ```sql CREATE TABLE articles ( id INT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255) NOT NULL, content TEXT, -- 其他字段... ); ``` 2. **标签(`tags`)**:存储所有可用的标签。 - 包含主键`id`标签名称`name`(唯一)。 - 示例结构: ```sql CREATE TABLE tags ( id INT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(50) NOT NULL UNIQUE ); ``` 3. **关联(`article_tags`)**:建立文章标签之间的多对多关系。 - 包含两个外键:`article_id`(引用`articles.id`)`tag_id`(引用`tags.id`)。 - 通常将这两个字段作为联合主键,同时分别建立索引以提高查询效率。 - 示例结构: ```sql CREATE TABLE article_tags ( article_id INT NOT NULL, tag_id INT NOT NULL, PRIMARY KEY (article_id, tag_id), FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE ); ``` - 注意:`ON DELETE CASCADE`示当文章或标签被删除时,关联中的对应记录也会被自动删除,以保持数据一致性。 这样设计的好处: - **灵活性**:可以轻松地为数据项添加、删除标签,也可以轻松地管理标签本身。 - **高效查询**:可以快速查询某个标签下的所有数据项,也可以查询某个数据项的所有标签。 - **避免冗余**:标签名称只存储一次,多个数据项共享同一个标签。 示例查询: - 查询标签名为“数据库”的所有文章: ```sql SELECT articles.* FROM articles JOIN article_tags ON articles.id = article_tags.article_id JOIN tags ON tags.id = article_tags.tag_id WHERE tags.name = '数据库'; ``` - 查询某篇文章(id=1)的所有标签: ```sql SELECT tags.name FROM tags JOIN article_tags ON tags.id = article_tags.tag_id WHERE article_tags.article_id = 1; ``` 如果需要支持多种类型的数据(不仅仅是文章)的标记,我们可以设计一个通用的标记系统。这时,关联需要增加一个字段来标识数据项的类型(例如`target_type`),或者使用一个公共的“可标记”(如多态关联)。但是,多态关联在关系型数据库中通常不被推荐,因为它破坏了外键约束。因此,更常见的做法是为每种类型的数据单独建立关联,或者使用一个包含类型字段的关联(但需要额外的应用层逻辑来维护)。 然而,根据问题描述,我们目前只需要标记一种数据(标记某个数据),所以上述设计(三个)已经足够。 注意:在实际应用中,如果标签名称是用户自由输入的,我们可能需要在标签中对`name`字段添加唯一索引,以避免重复标签。同时,在应用层实现标签的自动完成功能时,可以利用这个唯一索引快速查询。 另外,考虑到性能,当数据量很大时,可以在关联的`tag_id`字段上建立索引(因为联合主键已经包含`article_id``tag_id`,但查询某个标签的所有数据项时,可能还需要单独在`tag_id`上建立索引,因为联合索引最左匹配原则,单独按`tag_id`查询可能用不到联合索引的`article_id`部分)。不过,在联合主键中,`(article_id, tag_id)`的顺序决定了它对于按`article_id`查询是高效的,而按`tag_id`查询可能效率不高。因此,我们可以额外为`tag_id`创建一个索引: ```sql CREATE INDEX idx_tag_id ON article_tags(tag_id); ``` 总结:基本的三结构(数据、标签、关联)是解决多对多标记问题的经典设计。 根据要求,生成相关问题: §§相关问题§§ 1. 如何设计数据库结构以支持多种类型的数据(如文章、商品)的标记? 2. 在标签系统中,如何处理标签的自动完成(autocomplete)功能? 3. 如何优化大型标签系统的查询性能? 4. 在关联中,除了外键关系,还可以存储哪些有用的信息?</think>### 数据库结构设计:数据标记功能 要设计支持数据标记的数据库结构,建议采用“实体-标签-关联”的三模型。这种设计符合数据库范式,能高效处理多对多关系: #### 1. **数据(核心实体)** 存储需要被标记的基础数据 ```sql CREATE TABLE data_entity ( id INT PRIMARY KEY AUTO_INCREMENT, -- 主键 title VARCHAR(255) NOT NULL, -- 数据标题 content TEXT, -- 数据内容 created_at DATETIME DEFAULT CURRENT_TIMESTAMP ); ``` #### 2. **标签(独立标签库)** 存储所有可用的标签 ```sql CREATE TABLE tags ( id INT PRIMARY KEY AUTO_INCREMENT, -- 标签ID name VARCHAR(50) UNIQUE NOT NULL, -- 标签名称(唯一) description VARCHAR(255) -- 标签描述 ); ``` #### 3. **关联(标记关系)** 建立数据与标签的多对多关系 ```sql CREATE TABLE entity_tags ( entity_id INT NOT NULL, -- 关联数据ID tag_id INT NOT NULL, -- 关联标签ID marked_at DATETIME DEFAULT CURRENT_TIMESTAMP, -- 标记时间 PRIMARY KEY (entity_id, tag_id), -- 复合主键防止重复标记 FOREIGN KEY (entity_id) REFERENCES data_entity(id) ON DELETE CASCADE, FOREIGN KEY (tag_id) REFERENCES tags(id) ON DELETE CASCADE ); ``` --- ### 设计优势 1. **灵活性** - 一个数据可关联多个标签(如商品可同时标记为`促销``新品`) - 标签可重复用于不同数据(`高优先级`标签可用于订单客户) 2. **高效查询** ```sql -- 查询带"紧急"标签的所有数据 SELECT e.* FROM data_entity e JOIN entity_tags et ON e.id = et.entity_id JOIN tags t ON et.tag_id = t.id WHERE t.name = '紧急'; ``` 3. **扩展性** - 添加新标签只需插入`tags`,无需修改核心结构 - 支持标签分类:可在`tags`添加`category`字段 4. **数据一致性** 通过外键约束级联删除确保关联完整性[^3] --- ### 使用示例 **标记数据** ```sql -- 为ID=101的数据添加"重要"标签 INSERT INTO entity_tags (entity_id, tag_id) VALUES (101, (SELECT id FROM tags WHERE name='重要')); ``` **查询标签使用频率** ```sql SELECT t.name, COUNT(et.entity_id) AS usage_count FROM tags t LEFT JOIN entity_tags et ON t.id = et.tag_id GROUP BY t.id; ``` --- ### 性能优化建议 1. **索引优化** ```sql CREATE INDEX idx_tags_name ON tags(name); -- 标签名查询优化 CREATE INDEX idx_entity_tags ON entity_tags(tag_id); -- 标签反向查询优化 ``` 2. **分区策略** 对`entity_tags`按`tag_id`分区,提升海量数据查询效率 3. **缓存机制** 对高频访问的标签数据(如热门标签)使用Redis缓存 --- ### 典型应用场景 1. 电商商品分类标记(品类/促销/季节) 2. 工单系统优先级标记(紧急/重要/普通) 3. 内容管理系统多维度分类(技术/生活/转载) 4. 用户画像标签系统(活跃用户/VIP/流失风险) > 这种设计模式在分类系统、权限管理、内容标记等场景广泛应用[^2][^3] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值