课程 › 第 13 章 › 实验一
第 13 章 · 实验一 · 在第三节之后

实体与属性,局部图的合并

先比较系别作为属性和作为实体的两种表,再把采购部、销售部两张局部图得到的商品表,依次消除属性冲突、命名冲突和结构冲突,合成一张商品表。

环 境

MySQL 8.0 和 MySQL Workbench,机房电脑已装好。所有语句在 Workbench 的 SQL 编辑区中执行。

执 行

Ctrl+Enter 执行光标所在的一句,Ctrl+Shift+Enter 执行全部。Mac 上把 Ctrl 换成 ⌘。

注 意

输入法切到英文。表名、字段名不加引号,字符串值才用单引号。

本次实验的顺序 后一个实验用到前一个的结果

  1. 实验 1-1系别作为属性
  2. 实验 1-2系别作为实体
  3. 实验 1-3消除属性冲突
  4. 实验 1-4消除命名冲突
  5. 实验 1-5消除结构冲突
第一步

数据准备(必做)

新建 er13 库:学生表 stu_a 把系名、系主任、办公地点都当作学生的属性。另有两个局部 E-R 图各自得到的商品表 purchase_goods 和 sale_goods。

整段复制,全部执行下载 .sql
-- 第 13 章实验一数据:一张把系别信息当作属性的学生表,以及两个局部 E-R 图各自得到的商品表
-- 整段执行一次即可,想从头再做时重新执行。
DROP DATABASE IF EXISTS er13;
CREATE DATABASE er13 DEFAULT CHARACTER SET utf8mb4;
USE er13;

-- 系别作为学生的属性:系名、系主任、办公地点都写在学生表中
CREATE TABLE stu_a (
  sno CHAR(4) NOT NULL,
  sname VARCHAR(10),
  dept VARCHAR(10),
  mn VARCHAR(10),
  office VARCHAR(20),
  PRIMARY KEY (sno)
);
INSERT INTO stu_a VALUES
('s1', '王彤', '计算机', '刘伟', '信息楼 301'),
('s2', '苏乐', '信息', '王平', '信息楼 205'),
('s3', '林欣怡', '信息', '王平', '信息楼 205'),
('s4', '陶然', '计算机', '刘伟', '信息楼 301');

-- 采购部的局部 E-R 图得到的商品表
CREATE TABLE purchase_goods (
  gno CHAR(11) NOT NULL,
  gname VARCHAR(40),
  maker VARCHAR(40),
  intime DATETIME,
  PRIMARY KEY (gno)
);
INSERT INTO purchase_goods VALUES
('11000000112', '数据库原理及应用教程', '人民邮电出版社', '2020-02-11 11:15:25'),
('21200000109', '联想笔记本电脑 E14', '联想集团', '2020-03-02 09:30:00');

-- 销售部的局部 E-R 图得到的商品表,编号的名称和类型都与采购部不同
CREATE TABLE sale_goods (
  goods_id BIGINT NOT NULL,
  gname VARCHAR(40),
  price DECIMAL(9,2),
  PRIMARY KEY (goods_id)
);
INSERT INTO sale_goods VALUES
(11000000112, '数据库原理及应用教程', 45.50),
(21200000109, '联想笔记本电脑 E14', 5588.60);

SHOW TABLES;
1-1

系别作为属性

讲义 · 局部 E-R 图
目的
查看系别信息作为学生属性时的学生表,统计每个系的信息重复存放了几次。
前提
已执行数据准备,当前库是 er13。
  1. 查询 stu_a 的全部数据
  2. 按系名、系主任、办公地点分组,统计行数

参考答案

可以直接复制到 Workbench 执行
SELECT * FROM stu_a;
SELECT dept, mn, office, COUNT(*) AS 行数
FROM stu_a GROUP BY dept, mn, office;

语句解释

dept、mn、office系名、系主任、办公地点,都写在学生这一行中
GROUP BY系的信息相同的学生分成一组
问题系有了自己的描述信息,却跟着每个学生重复存放
执行后看:计算机系和信息系的系主任、办公地点各存了 2 次。系里有多少学生,就要存多少次。
1-2

系别作为实体

讲义 · 局部 E-R 图
目的
把系别改为实体,单独建系表 dept,学生表通过系名找到所在的系。
前提
已完成实验 1-1。
  1. 建立系表 dept,系名为主码
  2. 建立学生表 student,dept 为外码
  3. 从 stu_a 中搬入数据,用连接查询核对

参考答案

可以直接复制到 Workbench 执行
CREATE TABLE dept (
  dept VARCHAR(10) NOT NULL,
  mn VARCHAR(10),
  office VARCHAR(20),
  PRIMARY KEY (dept)
);
INSERT INTO dept SELECT DISTINCT dept, mn, office FROM stu_a;
CREATE TABLE student (
  sno CHAR(4) NOT NULL,
  sname VARCHAR(10),
  dept VARCHAR(10),
  PRIMARY KEY (sno),
  FOREIGN KEY (dept) REFERENCES dept (dept)
);
INSERT INTO student SELECT sno, sname, dept FROM stu_a;
SELECT s.sno, s.sname, d.dept, d.mn, d.office
FROM student s JOIN dept d ON s.dept = d.dept;

语句解释

实体有描述信息系有系主任、办公地点等描述信息,所以系别应当是实体
dept 表每个系只存一行
FOREIGN KEY (dept)学生与系是 n:1 的联系,在学生表中用外码表示
执行后看:dept 有 2 行,student 有 4 行,连接查询的结果与 stu_a 相同。
1-3

消除属性冲突

讲义 · 视图集成讲义 · 合并局部图
目的
两张商品表中商品编号的类型不同:采购部用 CHAR(11),销售部用 BIGINT。把销售部的改为 CHAR(11)。
前提
已完成实验 1-2。
  1. 查看两张商品表的结构
  2. 把 sale_goods 中 goods_id 的类型改为 CHAR(11)
  3. 再查看结构和数据

参考答案

可以直接复制到 Workbench 执行
DESC purchase_goods;
DESC sale_goods;
-- 统一为字符型,编号不参与计算
ALTER TABLE sale_goods MODIFY goods_id CHAR(11) NOT NULL;
DESC sale_goods;
SELECT * FROM sale_goods;

语句解释

属性冲突同一个属性在两张局部图中的类型、取值范围或单位不同
MODIFY修改字段的类型,已有的数据按新类型转换
用 CHAR编号只用来区分商品,不做加减运算,用字符型
执行后看:goods_id 的类型变为 char(11),数据中的编号不变。
1-4

消除命名冲突

讲义 · 合并局部图
目的
两张商品表中商品编号的名称不同:一个叫 gno,一个叫 goods_id。统一改为 gno。
前提
已完成实验 1-3。
  1. 把 sale_goods 的 goods_id 改名为 gno
  2. 查看结构

参考答案

可以直接复制到 Workbench 执行
ALTER TABLE sale_goods RENAME COLUMN goods_id TO gno;
DESC sale_goods;

语句解释

命名冲突异名同义:名称不同,指的是同一个属性。反过来同名异义也是命名冲突
RENAME COLUMNMySQL 8.0 中修改字段名的写法
案例中采购商品与销售商品也是异名同义,合并后统一叫商品
执行后看:sale_goods 的字段变为 gno、gname、price。
1-5

消除结构冲突

讲义 · 合并局部图
目的
两张商品表的属性组成不同,合并为一张商品表 goods,属性取两者的并集。
前提
已完成实验 1-4,两张表的编号名称和类型已经一致。
  1. 建立 goods,包含两张表的全部属性
  2. 用连接查询把两张表的数据合到 goods 中
  3. 查看 goods

参考答案

可以直接复制到 Workbench 执行
CREATE TABLE goods (
  gno CHAR(11) NOT NULL,
  gname VARCHAR(40),
  maker VARCHAR(40),
  intime DATETIME,
  price DECIMAL(9,2),
  PRIMARY KEY (gno)
);
-- 采购部的属性加上销售部的单价
INSERT INTO goods
SELECT p.gno, p.gname, p.maker, p.intime, s.price
FROM purchase_goods p LEFT JOIN sale_goods s ON p.gno = s.gno;
SELECT * FROM goods;

语句解释

结构冲突同一个实体在两张局部图中的属性组成不同
取并集合并后的实体包含两张图中的全部属性
LEFT JOIN以采购部的商品为准,按编号找到销售部的单价
执行后看:goods 有 2 行,每行有 5 个属性,既有生产厂家和入库时间,也有单价。
保山学院·人工智能教研室·曹鼎鼎