简介:通讯录管理系统数据库设计与实现文档是一份面向数据库课程设计与信息系统综合实践的完整配套资料,适合正在学习数据库原理或需要完成类似课程设计的学生与开发者参考。内容覆盖需求简介、功能概述、数据字典、数据结构设计、E-R 图转换、数据库模式设计、用户子模式设计、物理结构设计以及实施阶段的建库建表 SQL 语句,能够帮助读者完整走通从需求分析到数据库落地的全过程。资源包内含 1 个 doc 文档,整体大小约 422KB,文档结构清晰,按需求、概念结构、逻辑结构、物理结构、实施等章节组织,方便按步骤对照学习。已有 997 人浏览学习。通过该文档,读者可以掌握用户表、联系人表、分组表和拥有表的设计方法,理解主键、外键、Check 约束及默认值的使用,并可直接参考其中的 create database 与 create table 语句,快速应用到类似通讯录、客户管理等关系型数据库项目设计之中。
1. 一个能直接照做的通讯录管理系统数据库设计:从数据字典到建表SQL全是闭环的
第一次拿到这份《通讯录管理系统数据库设计与实现》的 doc 文档,我以为是网上那种只有 ER 图和几张表的半成品,翻到数据字典那页才发现它把整条链路都写全了:18 个数据项、4 张表、9 个用户子模式、10 段功能实现 SQL,连建库时主数据文件和日志文件怎么分盘都给了。这份资源最值钱的地方在于文档和代码对得上——需求分析里出现的每个字段,在后面数据字典、关系模型、建表语句里都能找到对应编号,这正是大多数课程设计最容易翻车的点。适合正在写数据库课程设计或毕业设计的同学,也适合想快速搭一个通讯录管理系统原型、但对 SQL Server 建库建表和主外键关系还不太熟的人。它不是科普读物,是一份能照着敲、能跑通的底稿。
2. 需求分析到数据字典:18 个数据项怎么变成 4 张表
2.1 功能概述:三类管理加两类查询,先锁住系统边界
通讯录管理系统的功能清单看着不长,但每一条都直接影响表结构。文档里的功能概述可以归纳成五类:用户基本信息管理、联系人基本信息管理、分组基本信息管理、用户分组管理、联系人查询。前三个是标准的增删改查,第四和第五是查询维度,分别对应"按归属查"和"按关键字查"。
这里有个容易被忽略的点:功能概述里出现"用户分组管理",意味着分组不是孤立的,它必须和用户、联系人产生关联。也就是说,表结构里一定要有一张能表达"哪个用户把哪个联系人放进哪个分组"的表,这就是后面 Own 表存在的理由。我拿到这类文档的习惯是先画一张功能到实体的映射表,确认每个功能点的操作对象是谁,再动数据字典。
这套功能对应的实体只有三个:用户、联系人、分组。联系是"用户拥有分组"和"分组拥有联系人",其中分组和联系人之间是多对多,必须拆成联系表。所以整套系统的表数量被功能锁死在 3 张实体表加 1 张联系表,不多不少。
2.2 数据字典:CHAR 长度、Date 类型和容易改错的字段
数据字典部分给了 18 个数据项,编号从 TXL-1 到 TXL-18。这里有一个阅读技巧:不要挨个看类型,而是先按归属分组。TXL-1 和 TXL-2 是分组属性,TXL-3 到 TXL-7 是用户属性,TXL-8 到 TXL-18 是联系人属性。
| 数据项编号 | 数据项名 | 类型 | 归属 |
|---|---|---|---|
| TXL-1 | G_number 分组号 | CHAR(10) | 分组 |
| TXL-2 | G_name 分组名 | CHAR(20) | 分组 |
| TXL-3 | ID 所属用户 | CHAR(10) | 用户 |
| TXL-4 | ID 账号 | CHAR(10) | 用户 |
| TXL-5 | Nickname 昵称 | CHAR(20) | 用户 |
| TXL-6 | Password 密码 | CHAR(20) | 用户 |
| TXL-7 | Qianming 签名 | CHAR(50) | 用户 |
| TXL-8 | L_number 联系人号 | CHAR(100) | 联系人 |
| TXL-9 | Name 姓名 | CHAR(20) | 联系人 |
| TXL-10 | Sex 性别 | CHAR(2) | 联系人 |
| TXL-11 | Concern 关系 | CHAR(10) | 联系人 |
| TXL-12 | Phone 手机号 | CHAR(11) | 联系人 |
| TXL-13 | Landline 座机号 | CHAR(10) | 联系人 |
| TXL-14 | CHAR(10) | 联系人 | |
| TXL-15 | CHAR(20) | 联系人 | |
| TXL-16 | Work 工作单位 | CHAR(30) | 联系人 |
| TXL-17 | Address 地址 | CHAR(30) | 联系人 |
| TXL-18 | Birthday 生日 | Date | 联系人 |
看这张表要注意两个地方。一是 TXL-8 联系人号给的 CHAR(100),到后面建表 SQL 里实际变成了 CHAR(10),这是文档自身的前后不一致,后面我会专门说怎么处理。二是 Sex 用了 CHAR(2),配合建表时的 CHECK 约束限制为"男"或"女",这种短枚举字段用定长 CHAR 是合理的,省空间还避免歧义。Birthday 是唯一的 Date 类型,其余全是 CHAR,说明这个系统里对日期没有做范围计算,只是存一个展示值,类型选得够用。
2.3 数据结构 DS-1 到 DS-4:实体、属性、联系怎么收纳
数据结构列表把散落的数据项收拢成四个结构:DS-1 Grouping 由 G_number、G_name、ID 组成;DS-2 Worker 由 ID、Nickname、Password、Qianming 组成;DS-3 Linkman 由 L_number、Name、Sex、Concern、Phone、Landline、QQ、Email、Work、Address、Birthday 组成;DS-4 Own 由 ID、G_number、L_number 组成。
这里能看出设计者的思路:前三个结构对应三张实体表,第四个结构专门放归属关系。注意 DS-4 把三个编号全收进来,这和后面建表时的复合主键设计是呼应的。数据结构是数据字典和 ER 图之间的过渡层,它决定了"哪些字段该待在同一张表里"。
有些同学拿到数据字典直接就开始建表,跳过数据结构这一步,后面 ER 图画起来就会乱。我一般会先做这个小聚合,再去看实体之间的关系,因为 DS-4 这种明显带三个外键的结构,已经暗示了后面必须有连接查询。
3. 从 ER 图到关系模型:四张表的主外键和子模式怎么定
3.1 ER 图转关系模型:实体表和关系表的映射规则
ER 图里出现的实体有用户、分组、联系人,属性包括账号、密码、昵称、个性签名、分组号、分组名、联系人号、姓名、性别、手机号等,联系部分是"用户拥有分组"和"分组拥有联系人"。
转关系模型的标准做法是:每个实体转成一张表,实体属性就是表的字段;多对多联系必须单独转成一张联系表。这个系统里分组和联系人就是多对多,所以产生了 Own 表。关系模型在文档里写成四张:
User(ID, Nickname, Password, Qianming) Grouping(G_number, G_name, ID) Linkman(Name, Sex, Concern, Phone, Landline, QQ, Email, Work, Address, Birthday) Own(ID, G_number, L_number)这段关系模型有个小问题:Linkman 里没有 L_number,但 Own 表却引用了 L_number,而且建表 SQL 里 Linkman 才有 L_number。这说明文档在关系模型阶段把 L_number 漏写了。另外 Grouping 里那个 ID 字段在建模阶段是有意让分组归属某个用户的,但到建表 SQL 阶段又没了,变成了 Grouping 只包含 G_number 和 G_name。这种前后不一致在课程设计文档里非常常见,复现的时候我一般以建表 SQL 为准,关系模型按建表 SQL 反推修正一遍。
3.2 主键与外键:Own 表复合主键为什么不能省
四张表的主外键设计是这份文档最核心的部分。Worker 表主键是 ID,Grouping 表主键是 G_number,Linkman 表在建表 SQL 里主键写的是 Name,Own 表主键是 (ID, G_number, Name) 三列复合。三个外键分别引用 Worker 的 ID、Grouping 的 G_number、Linkman 的 Name。
这里要特别说 Own 表的复合主键。它的作用是保证一行记录唯一确定"某个用户把某个联系人放在某个分组里",三个字段缺一个都会产生重复记录。比如只用 (ID, G_number) 做主键,那同一个分组里就放不下两个联系人;只用 (ID, L_number) 做主键,那一个联系人只能属于一个分组。复合主键在这个场景是必要的。
外键的引用顺序也讲究:必须先建 Worker、Grouping、Linkman,再建 Own,因为外键引用的表必须已经存在。这个顺序问题在实施阶段特别容易踩,后面避坑章节我会专门展开。
3.3 用户子模式 001 到 009:九个视图入口的规划思路
文档的第三部分给了 9 个用户子模式:001 用户分组显示、002 联系人查询、003 用户账号管理、004 联系人的修改、005 联系人的删除、006 联系人的添加、007 分组的创建、008 分组的修改、009 分组的删除。子模式的作用是给不同角色提供不同的数据视角,本质就是视图。
001 到 006 都绕不开 Linkman,007 到 009 只碰 Grouping。这说明权限控制的粒度是按功能划分的:管理联系人的角色不能碰分组结构,反之亦然。落地的时候,我一般会按功能域拆成三组视图:账号类视图管 Worker,分组类视图管 Grouping,联系人视图管 Linkman 和 Own。这个规划在实施阶段直接对应几个 CREATE VIEW 语句,后面我会给出可复制的写法。
4. 物理结构设计与实施:建库、建表、索引一次落地
4.1 存储结构与索引策略:数据文件日志文件分盘,两个唯一索引
物理结构设计这部分很多课程设计都是凑字数,这份文档反而写得比较实在。存储结构的要求是:主数据文件放在 D 盘,日志文件放在 E 盘,两个盘分开。这么做的好处是数据文件和日志文件的 IO 互不干扰,系统数据量上来之后,日志文件快速增长不会拖累数据文件的读写性能。
索引部分文档给的原则是:Worker、Linkman、Grouping 的主码 ID、G_number、Name 经常出现在查询条件和连接条件中,而且值唯一,所以在这些属性上建立唯一性索引。这里的"唯一性索引"用 SQL Server 的说法就是给主键建立唯一索引,主键约束本身会自动创建,不需要额外再写 CREATE UNIQUE INDEX。真正要注意的是:不要在 Phone、QQ 这些业务字段上乱建索引,联系人表数据量到几千条时全表扫描还能接受,建多了反而拖慢写入。
4.2 建库 SQL:size、maxsize、filegrowth 三组参数的意义
创建数据库的 SQL 在文档里直接给了,用的是 SQL Server 的语法:
CREATE DATABASE TXL ON ( NAME = TXL, FILENAME = 'D:\通讯录数据库\TXL.mdf', SIZE = 100MB, MAXSIZE = UNLIMITED, FILEGROWTH = 50MB ) LOG ON ( NAME = TXL_log, FILENAME = 'E:\通讯录数据库\TXL_log.ldf', SIZE = 3MB, MAXSIZE = 50MB, FILEGROWTH = 2MB );这段 SQL 里的三组参数值得逐个说。SIZE 是初始大小,主数据文件给了 100MB,对通讯录这种量级的系统来说偏大但安全,日志文件给了 3MB,因为日志会持续增长,初始值小一点没问题。MAXSIZE 控制上限,数据文件设成 UNLIMITED 意味着不设上限,日志文件限制 50MB 是防止日志无限膨胀占满磁盘。FILEGROWTH 是自动增长步长,数据文件每次涨 50MB,日志文件每次涨 2MB。比例上数据文件比日志文件大很多,这样频繁写入联系人数据的时候,数据文件不会频繁触发扩容。
提示:FILENAME 里的目录必须提前建好,SQL Server 不会自动创建文件夹,我第一次跑这段 SQL 就栽在这里,提示找不到路径,实际上是 D 盘和 E 盘下根本没有"通讯录数据库"这个目录。
4.3 建表 SQL:四张表的类型、约束和建表顺序
建表顺序要先 Worker、Grouping、Linkman,最后 Own,因为 Own 引用了前三张表。文档里的建表 SQL 四张表都全,我整理成可直接执行的版本:
-- 用户表 CREATE TABLE Worker ( ID CHAR(10) PRIMARY KEY, Nickname CHAR(20) NOT NULL, Password CHAR(20) NOT NULL, Qianming CHAR(50) ); -- 分组表 CREATE TABLE Grouping ( G_number CHAR(10) PRIMARY KEY, G_Name CHAR(20) NOT NULL ); -- 联系人表 CREATE TABLE Linkman ( L_number CHAR(10), Name CHAR(20) PRIMARY KEY, Sex CHAR(2) CHECK (Sex = '男' OR Sex = '女'), Concern CHAR(10) DEFAULT NULL, Phone CHAR(11) DEFAULT NULL, Landline CHAR(10) DEFAULT NULL, QQ CHAR(11) DEFAULT NULL, E_mail CHAR(30) DEFAULT NULL, Work CHAR(20) DEFAULT NULL, Address CHAR(30) DEFAULT NULL, Birthday DATE DEFAULT NULL ); -- 拥有表 CREATE TABLE Own ( ID CHAR(10), G_number CHAR(10), Name CHAR(20), PRIMARY KEY (ID, G_number, Name), FOREIGN KEY (ID) REFERENCES Worker(ID), FOREIGN KEY (G_number) REFERENCES Grouping(G_number), FOREIGN KEY (Name) REFERENCES Linkman(Name) );这段 SQL 有几个点要说明。第一,Worker 表的 Nickname 和 Password 都加了 NOT NULL,这符合注册场景——昵称和密码不能为空。Qianming 个性签名允许为空,因为用户可以不写签名。第二,Linkman 表的 Sex 用了 CHECK 约束,只允许"男"或"女",比在应用层判断靠谱。第三,除了主键和 NOT NULL 字段,其他联系人字段全部设了 DEFAULT NULL,保证插入联系人时只填姓名也能成功。第四,Own 表三个外键全部级联到主表,这里有一个隐患:删除 Grouping 里某个分组时,Own 表里引用这个分组的记录会报外键冲突,删除时必须按顺序处理。
细心的同学会发现,Linkman 表把 L_number 设为普通字段、Name 设为主键,这是文档原样。现实中同名联系人很常见,这个主键选型有问题,我后面避坑章节会详细讲怎么改。
4.4 填充测试数据:把注册和联系人插入跑一遍
表建好之后,文档在功能实现部分给了注册用户和插入联系人的 SQL,这些语句实际上就是最好的测试数据。我把核心的几条拎出来:
-- 注册一个用户 INSERT INTO Worker (ID, Nickname, Password, Qianming) VALUES ('00000', '用户1', '00000', '我的联系人我做主!'); -- 创建两个分组 INSERT INTO Grouping (G_number, G_name) VALUES ('0000000000', 'friends'); INSERT INTO Grouping (G_number, G_name) VALUES ('0000000001', 'family'); -- 插入两条联系人 INSERT INTO Linkman (L_number, Name, Sex, Concern, QQ, Phone, Landline, E_mail, Work, Address, Birthday) VALUES ('0000000001', '刘江涛', '男', NULL, '1234567891', '12345678901', NULL, NULL, NULL, NULL, NULL); INSERT INTO Linkman (L_number, Name, Sex, Concern, QQ, Phone, Landline, E_mail, Work, Address, Birthday) VALUES ('0000000002', '查孟博', '男', NULL, '1234567892', '12345678902', NULL, NULL, NULL, NULL, NULL); -- 建立归属关系 INSERT INTO Own (ID, G_number, L_number) VALUES ('00000', '0000000000', '0000000001'); INSERT INTO Own (ID, G_number, L_number) VALUES ('00000', '0000000000', '0000000002');这里注意一个问题:Own 表建表时第三列叫 Name,但插入时写的是 L_number。这个 SQL 直接执行会报"列名无效"。原因在于联系人表的主键是 Name 而不是 L_number,Own 表外键只能引用主键 Name。所以正确的插入应该是这样:
INSERT INTO Own (ID, G_number, Name) VALUES ('00000', '0000000000', '刘江涛');测试数据填充的关键是保持外键一致:Own 里出现的 ID 必须在 Worker 里存在,G_number 必须在 Grouping 里存在,Name 必须在 Linkman 里存在。少了任何一环,插入都会失败。
5. 避坑:建表顺序、保留字冲突与外键类型不一致
5.1 现象:CREATE TABLE 语句一执行就报语法错误
很多人照着文档敲建表 SQL,敲到分组表就报错。原因:GROUP 是 SQL Server 的保留字,直接叫 Group 会触发语法错误。这份文档里表名用的是 Grouping,绕开了保留字,但关系模型里写的是 Group,如果你按关系模型去建表,必然翻车。解决:表名要么用 Grouping,要么用方括号括起来,SQL Server 里 GROUP 这种保留字用[Group]可以强行建,但不推荐,后面每条查询都得带方括号,血泪教训。
5.2 现象:Insert into Own 报"列名无效"
文档 6.6 节的插入语句写的是INSERT INTO Own (ID, G_number, L_number),但 Own 表建表时的第三列叫 Name,不是 L_number。原因:文档在关系模型阶段用 L_number 表达联系人,到建表阶段却拿 Name 做了联系人主键,两个阶段字段没对齐。解决:统一改成插入 Name,或者彻底重构:把 Linkman 的主键换成 L_number,Own 表的第三列同步改成 L_number。我建议走后者,因为联系人主键用 Name 本来就是设计缺陷,后面会细说。
5.3 现象:插入第二个同名联系人时主键冲突
Linkman 表用 Name 当主键,意味着通讯录里不能出现两个同名的人。现实中同名同姓很常见,直接导致插入失败。原因:主键的唯一性约束不允许重复值。解决:把 L_number 设为主键,Name 降级为普通字段,同时在 Own 表里用 L_number 做外键。这样既保证联系人号唯一,也允许姓名重复。这是这份文档里最该改的一处设计。数据字典里 TXL-8 给 L_number 标的长度是 CHAR(100),建表时变成了 CHAR(10),实际用 10 位编号够用,但要保证全库统一。
5.4 现象:删除联系人时外键约束报错
直接执行DELETE FROM Linkman WHERE L_number = '0000000004'会报外键冲突,因为 Own 表里还有记录引用这个联系人。原因:Own 表的外键指向 Linkman,删除被引用记录时数据库拒绝执行。解决:必须先删 Own 再删 Linkman,文档里 6.8 节就是这么写的,先清 Own 再删联系人,顺序不能反。
DELETE FROM Own WHERE L_number = '0000000004'; DELETE FROM Linkman WHERE L_number = '0000000004';5.5 现象:按数据字典写 SELECT Email 报列名无效
数据字典 TXL-15 写的字段名是 Email,但建表 SQL 里用的是 E_mail,两个名字不一致,按数据字典去查就会报错。原因:建模阶段和建表阶段字段命名没统一。解决:以建表 SQL 为准,后续查询统一用 E_mail。如果强迫症想用 Email,就把建表 SQL 改了再重建表。这种命名不一致的问题在别人的文档里非常常见,拿到手第一步就是拿数据字典对着建表语句逐字段核对,别信文档,信能跑通的 SQL。
6. 把子模式落成视图:用一条 SQL 验证系统闭环
6.1 三个核心视图:把子模式从纸面落到 SQL
用户子模式 001 到 009 在文档里只有功能描述,没有实际创建语句。这一步我建议至少建三个视图:一个管用户账号,一个管分组联系人,一个管联系人查询。账号视图直接查 Worker,分组联系人视图要连三张表。
-- 用户账号管理视图 CREATE VIEW vw_account AS SELECT ID, Nickname, Password, Qianming FROM Worker; -- 用户分组显示视图 CREATE VIEW vw_grouping_contacts AS SELECT W.ID, W.Nickname, G.G_number, G.G_name, L.Name, L.Phone FROM Own O JOIN Worker W ON O.ID = W.ID JOIN Grouping G ON O.G_number = G.G_number JOIN Linkman L ON O.Name = L.Name; -- 联系人查询视图 CREATE VIEW vw_contact_query AS SELECT Name, Sex, Phone, QQ, E_mail, Work, Address, Birthday FROM Linkman;视图建好后,应用层就不用碰底层表结构了。比如查询某个用户的所有联系人,直接SELECT * FROM vw_grouping_contacts WHERE ID = '00000',底层怎么连表对上层透明。
6.2 用一条联表查询验证系统闭环
视图建完,最后用文档 6.10 节那个条件查询思路做一次联查验证。目标:查"friends"分组下所有联系人的姓名和手机号。
SELECT L.Name, L.Phone FROM Linkman L JOIN Own O ON L.L_number = O.L_number JOIN Grouping G ON O.G_number = G.G_number WHERE G.G_name = 'friends';注意这里我用了 L_number 做连接,前提是你已经按避坑章节把 Linkman 的主键和 Own 表的第三列改成了 L_number。如果保持文档原样的 Name 外键,连接条件就要换成JOIN Own O ON L.Name = O.Name。验证的标准是:能查出分组名对应的联系人列表,就说明从数据字典、ER 图、关系模型、建库建表到测试数据整条链路是通的。
从那以后我拿到任何数据库设计文档,都会先做一遍"三核对":数据字典对建表语句、关系模型对主外键、插入语句对表结构,全对上了才敢往下写代码。这份文档虽然有一些前后不一致的毛病,但胜在链路完整,该有的设计步骤一个不少,拿来当课程设计的底稿改改就能用,希望帮到你。
本文还有配套的精品资源,点击获取