
简介一份花店管理系统数据库设计的课程设计文档以大连交通大学数据库课程设计为背景完整呈现从需求分析到数据库实施的全过程。内容涵盖数据字典与流程图、概念结构设计中的E-R图与视图集成、逻辑结构设计中的关系模式转换与数据模型优化、物理设计中的索引与表空间以及触发器设计和数据载入等具体环节同时展示了IBM DB2环境下SQL语言在表的创建、修改、删除和查询中的实际应用并提供了可复用的表结构范例和撰写课程设计报告时所需的章节组织参考。资源为1个doc文件压缩包大小约630KB文档按章节组织目录清晰便于对照实验要求逐部分核对并补充到自己的设计说明中。目前已有261人学习浏览适合正在完成数据库原理课程设计、需要借鉴系统化设计思路与文档排版方式的学生使用。1. 数据库课程设计花店管理系统这份文档能帮你把完整流程跑下来数据库课程设计最容易翻车的不是 SQL 写不出来而是流程走不完整。这份《数据库课程设计-花店管理系统设计.doc》来自大连交通大学用 IBM DB2 把一个花店管理系统数据库从需求分析、数据字典、E-R 图、关系模式转换、范式优化一直做到表空间、索引、触发器、视图和三用户权限管理每一步都有对应的表和 SQL。它适合正在准备数据库课程设计、想知道“需求分析到底要写多细”的学生也适合想找一个现成 DB2 案例改造成自己业务场景的从业者。照着这份文档的顺序走能少走一半弯路。2. 需求分析与数据字典七个基本表是怎么从业务里抽出来的我拆这份文档的时候第一步看的是它的需求分析。许多人做数据库设计习惯先建表但原文把这件事做反过来了先画业务流程再列查询需求最后才落到表结构。这个顺序决定了后期 E-R 图和关系模式是不是一次过所以值得单独拆一章。2.1 业务流程与系统模块先画流程图再谈建表原文把花店业务抽象成三个环节到花市采购鲜花、花店对鲜花标价、柜台销售鲜花。这三个环节看着简单却是整个系统的基础。围绕它列出四组查询需求采购需求要查花市编号、花市名称、花市地址鲜花销售需求要查花店编号、鲜花名称、销售额店员信息需求要查店员编号、店员姓名、工资鲜花信息需求要查鲜花名称、价格、花语。每组查询需求对应的都是将来某张表里的字段。我在做设计时也沿用这个习惯每写出一条查询功能就追问一句“这句话查的是哪张表、哪几个属性”需求分析做完表结构已经有一半摆在明面上了。原文在模块划分上用的是结构化分析方法SA 方法自顶向下把系统拆成花市信息系统、花店信息系统、店员信息系统、鲜花信息系统四个子系统。这也为概念设计阶段的分 E-R 图埋了伏笔每个子系统在后续都能对应到一到两个局部 E-R 图。提示需求分析阶段不是把功能罗列出来就完了而是要形成数据字典、业务流程图、系统模块图这三个交付物。少了任何一个后面做概念设计时都会返工。2.2 数据字典七个基本表覆盖花店全业务数据字典是需求分析的落点它的作用是把业务术语翻译成数据结构定义。原文给出的系统包含七个基本表花市信息表、花店信息表、会员信息表、店员信息表、鲜花信息表、花店采购信息表和鲜花销售信息表。数据结构名含义说明组成花市定义了花市的有关信息花市编号花市名称花市地址花店定义了花店的有关信息花店编号花店名称花店地址花店电话花店采购信息表定义了花店采购的有关信息花市编号花店编号店员定义了店员的有关信息店员编号店员姓名工资花店编号鲜花定义了鲜花的有关信息鲜花名称价格花语鲜花销售信息表定义了鲜花销售的有关信息鲜花名称花店编号销售额会员信息表定义了会员的相关信息数据字典中列出后文未展开这七个表在实际拆分时分成两类花市、花店、店员、鲜花是实体表代表业务里真实存在的对象花店采购信息表和鲜花销售信息表是联系表单独拆出来是因为采购和销售都是多对多的关系。有个容易被忽视的坑原数据字典里出现过会员信息表但后文概念结构与逻辑结构里没有继续展开。如果你照这份文档做作业要么把会员表加进 E-R 图并补充关系模式要么就从数据字典里删掉否则答辩时老师指着数据字典问“会员表去哪了”的时候会很难解释。2.3 实体与联系采购、销售、工作三条线决定外键走向数据字典列完之后接下来要抽实体与联系。原文的实体很清楚花市、花店、店员、鲜花。关键是联系一共三条花店与花市的联系是采购多对多。一家花店可以去多个花市进货一个花市也给多家花店供货。多对多关系在逻辑设计阶段需要单独建一张联系表也就是花店采购信息表。花店与鲜花的联系是销售也是多对多而且这个联系带了一个属性“销售额”。带属性的多对多联系同样要单独建表把销售额作为这张表的属性字段而不是塞进花店表或鲜花表。花店与店员的联系是工作一对多。一个花店有多名店员一名店员只属于一个花店。一对多关系不会单独建表而是把“一”这一端的主键放到“多”这一端也就是把花店编号放进店员表做外键。这三条线分清之后后面 E-R 图转关系模式就变成了机械操作。需求阶段最怕的是“先建表、后想关系”那样往往会漏掉外键或者把多对多错误地用一个外键字段表达后面调整起来比重新设计还麻烦。3. 概念结构与逻辑设计E-R 图怎么转成第三范式关系模式概念结构设计的任务是把需求分析的结果抽象成 E-R 图逻辑结构设计则是把 E-R 图转换成关系模式再通过范式检查把它优化成可落地的表结构。这两步在数据库课程设计里通常连着做也是最容易被当成“画图凑页数”的部分。3.1 概念设计分 E-R 图两两集成冲突更好定位原文在概念设计阶段采用自底向上的方法需求分析是自顶向下拆概念设计反过来自底向上合。先分别设计花市、花店、店员、鲜花这几个局部 E-R 图再把它们集成成全局 E-R 图。视图集成有两种常见方式一种是所有分 E-R 图一次性集成另一种是每两个集成一次。原文选的是两两集成理由很实际一次性集成多张图时命名冲突、属性冲突、结构冲突会同时出现很难定位是哪两个局部视图之间的矛盾。两两集成时一次只处理一对矛盾排错成本低得多。集成阶段要处理三类冲突命名冲突同一个概念在不同分 E-R 图里叫法不一致属性冲突同一个属性的类型或单位不一致结构冲突同一实体在不同视图中被抽象成实体或属性不一致。这个项目里体现最明显的是花店编号它在花市、店员、采购、销售四个局部视图里反复出现如果哪个局部视图里把花店编号写成了花店ID集成时就会出现命名冲突。课程设计阶段把这类对账做干净转关系模式时会省很多事。3.2 E-R 图转关系模式三种对应关系与主外键分配E-R 图向关系模型转换有一套固定规则用了这么多年基本没变过1:1 联系外键可以放在任意一端。1:n 联系把“一”方的主键放“多”的一方作为外键。m:n 联系单独建立一个关联表把两端的主键都放进去两个字段共同组成联合主键。如果联系本身带有属性属性放在关联表里。按这个规则原文档的花店系统转换结果如下花市花市编号花市名称花市地址 花店花店编号花店名称花店地址花店电话 花店采购信息表花市编号花店编号 店员店员编号店员姓名工资花店编号 鲜花鲜花名称价格花语 鲜花销售信息表花店编号鲜花名称销售额这套转换里最值得注意的就是花店采购和鲜花销售两张表的主键都是联合主键。采购表的花市编号花店编号缺一不可销售表的花店编号鲜花名称缺一不可。如果建表时只给其中一个字段设主键就会出现同一个花店只能进一次货、同一朵花只能卖一次的荒唐约束。3.3 数据模型优化从数据依赖到第三范式转换完关系模式不等于设计完成还要做范式检查。原文在这一节做了数据依赖分析先列出每个关系的函数依赖再做极小化处理消除冗余最后确认每个模式都达到第三范式。以花店为例数据依赖是花店编号→花店名称、花店编号→花店地址、花店编号→花店电话主键决定所有的非主属性没有部分依赖也没有传递依赖BCNF 都够得上。店员表的主键是店员编号同时店员编号→花店编号花店编号是引用花店表的外键不存在传递依赖。鲜花表的主键是鲜花名称价格和花语都直接依赖鲜花名称也是第三范式。这里想强调一个常见误解E-R 图转出来的关系模式不天然满足第三范式。比如如果把花店地址放进店员表就会出现“同一花店的多个店员重复保存同一地址”的冗余并且修改地址时必须更新多条记录这属于更新异常。第三范式检查的本质就是把这种不合理的依赖拆掉。原文档里“极小化处理、消除冗余”说的就是这个过程。3.4 表结构落地字段类型、长度与约束选择的依据逻辑结构设计最终要落成表结构。六张核心表的结构定义如下表名字段类型长度约束花市花市编号char10主键花市花市名称varchar20非空花市花市地址varchar50非空花店花店编号char10主键花店花店名称varchar20非空花店花店电话varchar20非空花店花店地址varchar50非空店员店员编号char10主键店员店员姓名varchar20非空店员工资decimal—非空店员花店编号char10外键鲜花鲜花名称varchar20主键鲜花价格decimal—非空鲜花花语varchar20非空花店采购信息表花市编号char10联合主键、外键花店采购信息表花店编号char10联合主键、外键鲜花销售信息表花店编号char10联合主键、外键鲜花销售信息表鲜花名称varchar20联合主键、外键鲜花销售信息表销售额decimal—非空选型上有几个点可以展开说。编号用 char(10) 而不是自增整数原因在于这些编号承载业务语义比如 HS001 代表某花市用自增列会丢失可读性。如果确实想用自增DB2 里的写法是 GENERATED ALWAYS AS IDENTITY但这份设计里七张表的主键几乎全是有业务含义的编号和名称不依赖自增更合理。价格和销售额用 decimal 而不用 float是因为浮点数存在二进制精度误差金额累计时会出现 0.10.2 不等于 0.3 的问题课程设计里也许看不出来但做财务相关需求时这是硬伤。4. 物理设计与数据库实施表空间、索引、触发器与权限怎么配物理设计是数据库课程设计里学生最容易当黑匣子跳过的部分到处都是“玄学”参数。原文这部分写得比较实表空间、索引、建表、触发器、视图、三用户权限都给了具体 SQL。我按执行顺序拆一遍先表空间再索引再建表与载入数据最后是触发器、视图和权限。4.1 表空间DMS 文件容器还是 SMS 系统容器表空间是 DB2 里表数据和索引数据的物理存放区域。原文建了多个表空间SQL 如下connect to ag02wmn; create regular tablespace dms02 managed by database using (file d:\dms\dms02 14) extentsize 2; create long tablespace dms03 managed by database using (file d:\dms\dms03 728) extentsize 8; create regular tablespace dms04 managed by database using (file d:\dms\dms04 22) extentsize 2; create regular tablespace dms05 managed by database using (file d:\dms\dms05 16) extentsize 2; create regular tablespace dms06 managed by database using (file d:\dms\dms06 40) extentsize 4; create regular tablespace sms01 managed by system using (d:\sms\sms01,d:\sms\sms02) extentsize 4;这里的 managed by database 表示 DMS数据库管理空间后面指定的是文件容器managed by system 表示 SMS系统管理空间后面指定的是目录容器。DMS 的优势是空间分配可控、能独立管理文件SMS 的优势是不用预先分配固定大小的文件直接使用操作系统目录的空闲空间。两个参数值得注意。extentsize 是扩展块大小单位是页默认页大小 4K 时 extentsize 2 表示一次分配 8K。到底设多大要看数据访问模式连续扫描多的场景可以设大一点减少 IO 次数随机访问多的场景设小一点避免浪费空间。文件容器后面的数字是初始分配页数比如 dms02 分配了 14 页dms03 分配了 728 页。提示建表空间的目标之一是让表和索引存储在不同表空间这样读写竞争会分散。后面建表时可以用 IN dms02 指定表所在表空间用 INDEX IN dms04 指定索引所在表空间。4.2 索引建两个足够别把每张表都建一遍索引的选择不是越多越好而是要匹配查询需求。原文建了两个索引一个建在花市表的花市名称上一个建在店员表的店员姓名上CREATE INDEX user.flower_market_idx ON user.flower_market (market_name ASC) PCTFREE 10 MINPCTUSED 10 ALLOW REVERSE SCANS PAGE SPLIT SYMMETRIC COLLECT SAMPLED DETAILED STATISTICS; CREATE INDEX user.staff_idx ON user.staff (emp_name ASC) PCTFREE 10 MINPCTUSED 10 ALLOW REVERSE SCANS PAGE SPLIT SYMMETRIC COLLECT SAMPLED DETAILED STATISTICS;PCTFREE 10 表示每个索引页预留 10% 的空间给后续 UPDATE 使用避免页分裂过于频繁。MINPCTUSED 10 表示当索引页空间使用率降到 10% 以下时触发页面合并主要是为了回收空间。ALLOW REVERSE SCANS 允许反向扫描意味着 ORDER BY DESC 也能走到这个索引。PAGE SPLIT SYMMETRIC 是 DB2 管理页分裂的一种策略让叶子节点分裂时更对称从而减少索引碎片。COLLECT SAMPLED DETAILED STATISTICS 是建索引时顺便采样收集统计信息优化器拿到统计信息后才能选出合理的访问计划。为什么只建这两个索引原文档的查询需求里按花市名称查花市、按店员姓名查人员是典型查询适合建索引。而价格、销售额这类字段如果经常出现在 WHERE 条件里也值得建但课程设计演示场景不需要全覆盖。索引不是免费的每多一个索引INSERT、UPDATE、DELETE 的代价都会增加所以只给高频查询路径建索引是一条通用原则。4.3 建表与数据载入主键、外键、Check 一次写对原文档要求至少六张表、每张表都有主键、设必要的外键、设计一个 Check 约束、至少建一个视图。我在还原时把表名和字段名换成英文字段含义对应原文档避免 DB2 里中文字段名在部分编码环境下出问题。CREATE TABLE user.flower_market ( market_id CHAR(10) NOT NULL, market_name VARCHAR(20) NOT NULL, market_addr VARCHAR(50) NOT NULL, CONSTRAINT pk_market PRIMARY KEY (market_id) ); CREATE TABLE user.flower_shop ( shop_id CHAR(10) NOT NULL, shop_name VARCHAR(20) NOT NULL, shop_tel VARCHAR(20) NOT NULL, shop_addr VARCHAR(50) NOT NULL, CONSTRAINT pk_shop PRIMARY KEY (shop_id) ); CREATE TABLE user.staff ( emp_id CHAR(10) NOT NULL, emp_name VARCHAR(20) NOT NULL, salary DECIMAL(8,2) NOT NULL, shop_id CHAR(10) NOT NULL, CONSTRAINT pk_staff PRIMARY KEY (emp_id), CONSTRAINT fk_staff_shop FOREIGN KEY (shop_id) REFERENCES user.flower_shop (shop_id), CONSTRAINT ck_staff_salary CHECK (salary 0) ); CREATE TABLE user.flower ( flower_name VARCHAR(20) NOT NULL, price DECIMAL(8,2) NOT NULL, flower_lang VARCHAR(20) NOT NULL, CONSTRAINT pk_flower PRIMARY KEY (flower_name) ); CREATE TABLE user.purchase ( market_id CHAR(10) NOT NULL, shop_id CHAR(10) NOT NULL, CONSTRAINT pk_purchase PRIMARY KEY (market_id, shop_id), CONSTRAINT fk_purchase_market FOREIGN KEY (market_id) REFERENCES user.flower_market (market_id), CONSTRAINT fk_purchase_shop FOREIGN KEY (shop_id) REFERENCES user.flower_shop (shop_id) ); CREATE TABLE user.sale ( shop_id CHAR(10) NOT NULL, flower_name VARCHAR(20) NOT NULL, amount DECIMAL(10,2) NOT NULL, CONSTRAINT pk_sale PRIMARY KEY (shop_id, flower_name), CONSTRAINT fk_sale_shop FOREIGN KEY (shop_id) REFERENCES user.flower_shop (shop_id), CONSTRAINT fk_sale_flower FOREIGN KEY (flower_name) REFERENCES user.flower (flower_name) );建表逻辑有几个关键点。purchase 表和 sale 表的主键都是联合主键这是多对多联系转成关系模式的必然结果。staff 表里的 shop_id 是外键引用 flower_shop 的 shop_id这一条对应概念设计里“花店与店员是一对多”的关系。Check 约束做在了 salary 0 上如果工资传入负数会直接报错。这个约束和后面触发器要实现的工资校验在功能上重叠课程设计里如果两个都做了演示时正好可以说明“约束做静态检查触发器做更复杂的逻辑控制”。数据载入三种方式区别很实际。少量演示数据直接 INSERTINSERT INTO user.flower_market VALUES (HS001, 城东花市, 城东大道18号); INSERT INTO user.flower_shop VALUES (HD001, 花语花店, 010-88888888, 中心街1号); INSERT INTO user.staff VALUES (YG001, 张丽, 4500.00, HD001); INSERT INTO user.flower VALUES (红玫瑰, 15.00, 我爱你); INSERT INTO user.sale VALUES (HD001, 红玫瑰, 1200.00);数据量大时用 IMPORT 从文件载入IMPORT FROM data.del OF DEL INSERT INTO user.flower;IMPORT 会执行约束检查和触发器。如果换成 LOAD速度快得多但默认不检查约束、不触发触发器。课程设计里如果你在演示触发器就千万别用 LOAD 去灌数据否则触发器白写了。增删改查是数据库课程设计的必考项演示 UPDATE 和 DELETE 时要注意操作顺序。比如把红玫瑰调价 20%UPDATE user.flower SET price price * 1.2 WHERE flower_name 红玫瑰;删除花市时就不能直接删 flower_market 里的记录因为 purchase 表引用了它。必须先删除引用它的 purchase 记录再删花市否则会报外键约束违规。4.4 触发器与视图用视图限权限用触发器守数据原文档要求至少建一个视图目的是通过视图把表的敏感字段隔离开。这里建一个按花店汇总销售额的视图CREATE VIEW user.v_shop_sales AS SELECT shop_id, SUM(amount) AS total_sales FROM user.sale GROUP BY shop_id;视图的价值在于授权时可以只给用户查询视图的权限而不是直接给底层表权限。比如老板角色只看每个花店的总销售额看不到单笔销售明细那么只需要给这个视图授权。触发器部分课程设计里最稳妥的写法是 BEFORE INSERT 触发器做校验CREATE TRIGGER user.trg_salary_check BEFORE INSERT ON user.staff REFERENCING NEW AS n FOR EACH ROW WHEN (n.salary 0) SIGNAL SQLSTATE 75001 SET MESSAGE_TEXT 工资不能为负;这个触发器和前面的 CHECK 约束功能重复但实现的思路不同。CHECK 是纯粹的静态约束而 SIGNAL SQLSTATE 可以在触发器里做更复杂的判断。比如将来要校验“新插入的工资不能低于同店员工平均水平”触发器可以写子查询CHECK 就不行。这是触发器存在的真正原因它能跨行、跨表做校验。注意触发器里不要 UPDATE 当前表。AFTER INSERT 触发器如果又去 UPDATE 同一张表会再次触发这个触发器造成递归DB2 会在检测到过深嵌套时报错。常见做法是校验类逻辑用 BEFORE 触发器加 SIGNAL或者让 AFTER 触发器去更新统计表、日志表。4.5 用户权限SYSADM、DBADM、表级特权三层权限怎么落原文档的系统实验要求建三个用户目标是跑通 DB2 的三层权限体系。第一层是实例级权限通过把 user1 和 db2admin 一起加入 admin 组让 admin 组拥有 SYSADM 权限。在 Windows 上这是在操作系统层面建组、加用户然后在 DB2 里确认db2 update dbm cfg using sysadm_group admin第二层是数据库级权限给 user2 授 DBADMGRANT DBADM ON DATABASE TO USER user2;第三层是对象级权限把某张表的所有特权授给 user3GRANT ALL PRIVILEGES ON TABLE user.flower TO USER user3;这里容易漏一个细节如果通过视图做权限隔离要把对视图的权限也显式授予否则用户能看到视图定义一查询却报没有权限。DB2 的权限判断是叠加的视图授权和基表授权要一起检查。课程设计里演示权限时建议把三个用户的权限矩阵写成表格SYSADM 管实例、DBADM 管数据库、表级权限管对象答辩时被问到的概率极高。5. 避坑DB2 课程设计里最典型的五个翻车现场这段是照着原文档实操 DB2 时的血泪经验。每一条都是“现象→原因→解决”的顺序省得你再踩一遍。5.1 建表报 SQL0204N其实不是表不存在是权限没到位现象用普通用户身份运行建表 SQL报错显示某个表名或模式名无效比如 SQL0204N。你明明确认表不存在但就是建不了。原因DB2 会把你登录用户名作为默认模式名如果该用户没有在对应模式下的建表权限语法错误背后其实是权限问题。解决先确认当前登录用户是谁用db2 VALUES CURRENT USER看一下。建表时要么显式写成CREATE TABLE user2.flower(...)要么用有建表权限的账号连接数据库。万一权限都没配好先执行GRANT CREATEIN ON SCHEMA user2 TO USER user2再跑建表 SQL。5.2 表空间容器路径不存在DMS 文件容器不会自动建目录现象执行 create regular tablespace 时提示容器无法访问或者表空间状态异常list tablespaces show detail看到 State 不是 0x0000。原因DMS 表空间的 file 容器指定了d:\dms\dms02但d:\dms目录根本不存在DB2 不会替你创建目录。SMS 表空间也有同样的坑指定的目录必须先建好。解决在命令行先把目录创建好再执行建表空间mkdir d:\dms mkdir d:\sms建完用db2 list tablespaces show detail检查状态State 为 0x0000 才表示正常在线。另外文件容器的大小参数是页数不是字节数别拿字节数硬填。5.3 外键插入顺序先插父表还是先插子表现象向 staff 表插入一条店员记录报外键约束违规提示 shop_id 在父表中不存在错误码 SQL0530N。原因staff 表的 shop_id 引用了 flower_shop 表的 shop_id但 flower_shop 表里还没有这条花店记录。数据库的外键约束不允许“引用不存在的父行”。解决插入时先插父表再插子表。也就是说必须先 INSERT flower_shop再 INSERT staff。删除时顺序反过来先删子表记录再删父表记录。这个顺序在做增删改查演示时要提前规划好不要在现场随机操作。5.4 联合主键漏了一半同一个花店只能卖一种花现象向 sale 表插入HD001红玫瑰后又插入HD001百合第二次报主键冲突但这两条记录业务上明明都合法。原因建表时只把 shop_id 设成了主键鲜花名称没有进主键。于是同一花店只能存在一条销售记录这显然不符合业务。解决把 sale 表的主键设为shop_id, flower_name联合主键purchase 表同理设成market_id, shop_id。建完表后用db2 describe table user.sale看一下主键列确认两个字段都在。5.5 char(10) 自动补空格条件查询诡异失效现象WHERE market_id HS001查不出数据或者查出来的结果看着对但字符串连接后多了空格怎么比对都不相等。原因char(10) 是定长类型存储时右边自动补空格到 10 字节。字面量 HS001 只有 5 个字符和补满空格的字段值直接比较结果不相等。varchar 不存在这个问题但原文档设计里编号用的全是 char。解决编号字段尽量用 varchar 替代 char如果必须用 char查询时用TRIM(market_id) HS001或者把字面量补足到等长。从那以后我建表默认把编码类字段都设成 varchar只有长度绝对固定且不会参与高频比较的字段才用 char。6. 把课程设计从能跑做成能答辩验证与扩展6.1 用系统目录表和工具验证设计是否真的成立数据库建完之后不要急着截图交作业先用系统目录表自检一遍约束是不是建全了SELECT tabname FROM syscat.tables WHERE tabschema USER; SELECT trigname FROM syscat.triggers WHERE trigschema USER;这两条语句分别确认所有表都已创建、触发器已注册。索引是否被查询真正使用用 db2expln 生成访问计划看一眼确认 WHERE 条件里的字段走的是索引扫描而不是整表扫描。表空间状态用db2 list tablespaces show detail复查全部 0x0000 才算正常。6.2 把花店系统替换成你自己的业务场景这套七表结构可以完整迁移到别的场景。比如做奶茶店管理系统花市换成供应商花店换成门店店员换成员工鲜花换成奶茶采购和销售两张联系表保持不变字段改名后数据字典、E-R 图、范式分析、触发器、权限这五层全部可以复用。替换时唯一要注意的是主外键跟着实体走不要只改表名不改引用关系。6.3 答辩前要准备的三张牌概念设计的 E-R 图要能讲清楚为什么要单独建采购表和销售表第三范式检查要能说出至少一个“如果不拆分会怎样”的例子触发器要讲明白为什么用 SIGNAL SQLSTATE 而不是只用 CHECK 约束。这三张牌打出去答辩基本稳了。完整的《数据库课程设计-花店管理系统设计.doc》里需求分析、数据字典、E-R 图、表结构定义和 DB2 操作 SQL 是成体系串好的可以直接拿来做模板按自己业务改名字即可。我每次做完数据库设计都会在提交前把主外键、触发器、视图权限重新过一遍确认表和表的关系不是脑补出来的而是真在 DB2 里跑通过的——希望帮到你。本文还有配套的精品资源点击获取