在重新构建数据库方面我遇到了一些麻烦。我有一个book
数据库,其中仅包含一个表格,所有作者的数据都包含在每本书之后。我正在尝试重新制作该数据库,以具有一个author
表和一个book
表。
我使用以下命令制作了author
表:
CREATE TABLE AUTHORS
AS SELECT AUTHOR_NAME, AUTHOR_SURNAME, AUTHOR_BIRTHDATE
如果我现在想重新制作book
表,如何添加外键,以便每本书的作者都是正确的?也就是说,如果原始book
表上的第一项是:
ISBN1 Title1 Author_Name1 Author_Surname1 Author_Birthdate1
如何将这些数据导入新表,以便新作者字段(外键)引用author表中的正确条目?抱歉,这很令人困惑。
答案 0 :(得分:1)
您正在寻找将现有表格分为两个表格,一个用于存储作者,另一个用于书籍。为了使其正常工作,您需要为每个作者创建一个唯一的ID。这是逐步的方法。
假定以下旧数据结构:
create table old_books (
isbn NUMBER(13, 0),
title VARCHAR2(200),
author_name VARCHAR2(200),
author_surname VARCHAR2(200),
author_birthdate DATE
);
此示例数据:
ISBN | TITLE | AUTHOR_NAME | AUTHOR_SURNAME | AUTHOR_BIRTHDATE ------------: | :----- | :---------- | :------------- | :--------------- 1000000000001 | book 1 | name 1 | surname 1 | 01-MAR-90 1000000000002 | book 2 | name 2 | surname 2 | 01-MAR-95 1000000000003 | book 3 | name 1 | surname 1 | 01-MAR-90
首先,让我们为authors
创建并馈送新的数据结构(请注意,您不想使用CREATE TABLE AS SELECT ...
,因为这不允许您添加约束或其他有用的选项)。
要生成唯一的作者ID,我们使用IDENTITY
功能(从Oracle 12c开始可用-如果没有此功能,则需要创建序列和触发器)。
在旧有数据中,我们假设每个作者均通过其姓名,姓氏和出生日期来唯一标识:
CREATE TABLE authors (
id NUMBER GENERATED ALWAYS AS IDENTITY,
name VARCHAR2(200),
surname VARCHAR2(200),
birthdate DATE,
PRIMARY KEY (id)
);
INSERT INTO AUTHORS (name, surname, birthdate)
SELECT DISTINCT author_name, author_surname, author_birthdate FROM old_books;
2 rows affected
SELECT * FROM authors;
ID | NAME | SURNAME | BIRTHDATE
-: | :----- | :-------- | :--------
1 | name 1 | surname 1 | 01-MAR-90
2 | name 2 | surname 2 | 01-MAR-95
有了第一个表后,我们现在可以创建books
表。它包含一个引用authors
表的主键的外键。要填充表,我们需要将旧表与新的authors
表连接起来以恢复每个作者的ID:
CREATE TABLE books (
isbn NUMBER(13, 0),
title VARCHAR2(200),
author_id NUMBER,
CONSTRAINT book_author FOREIGN KEY(author_id) REFERENCES authors(id),
PRIMARY KEY (isbn)
);
INSERT INTO books(isbn, title, author_id)
SELECT ob.isbn, ob.title, a.id
FROM old_books ob
INNER JOIN authors a
ON a.name = ob.author_name
AND a.surname = ob.author_surname
AND a.birthdate = ob.author_birthdate;
3 rows affected
SELECT * FROM books;
ISBN | TITLE | AUTHOR_ID
------------: | :----- | --------:
1000000000001 | book 1 | 1
1000000000002 | book 2 | 2
1000000000003 | book 3 | 1
全部准备好!在适当的约束条件下,数据可以在两个表之间正确分配。我们可以通过查询将两个表连接起来:
SELECT b.isbn, b.title, a.name, a.surname, a.birthdate
FROM authors a
INNER JOIN books b ON a.id = b.author_id;
ISBN | TITLE | NAME | SURNAME | BIRTHDATE
------------: | :----- | :----- | :-------- | :--------
1000000000001 | book 1 | name 1 | surname 1 | 01-MAR-90
1000000000002 | book 2 | name 2 | surname 2 | 01-MAR-95
1000000000003 | book 3 | name 1 | surname 1 | 01-MAR-90
答案 1 :(得分:0)
您说作者的名字加姓氏是您作者表的主键。这是一种有效的方法。如果两位作者的名字相同,则必须找到“ John” +“ Smith”和“ John R”之类的解决方案。 +'Smith'或'John'+'Smith(幻想作者)'。这被称为自然复合密钥,尽管它不是完美的密钥,因为我们可能不得不处理提到的重复名称。另一方面,存在存在个同名作者,因此我们可能会立即遇到此问题;-)
书籍通过其ISBN进行标识,由于没有重复项,因此可以提供更好的自然键。 (仅当您要添加没有ISBN的非常古老的书籍或自售书籍时,才需要创建伪造的ISBN。)
为了使您的书引用作者,您必须包括完整的密钥,在此是第一位,也要姓。这不是多余的,因为这是在数据库中标识作者的关键。
CREATE TABLE books AS SELECT isbn, title, author_name, author_surname FROM old_table;
ALTER TABLE books ADD CONSTRAINT fk_book_author FOREIGN KEY (author_name, author_surname)
REFERENCES authors (author_name, author_surname);
一种替代方法是引入代理(即技术)密钥。您将为每本书和每位作者生成一个ID(数字)并与他们合作。 (这意味着book表中将包含一个author_id。)但是对于一个好的数据库,您仍然应该考虑一下自然标识行的内容。这使以后编写查询的人更容易。 (例如,有人要求选择作者列表和他们写的书的数量。如何编写该查询?只显示名字和姓氏就足够了吗?或者我们可以在两行后面写上“ John Smith | 5”和“ John Smith | 2“,询问者说他们不能使用这个模棱两可的结果吗?)即使提供了代理密钥,您也应该对自然密钥有唯一的约束(如果有)。对于带有可选ISBN的图书,它可以是书名+ author_id,对于作者,它可以是名字+姓氏+出生日期。
顺便说一句:有些书的作者不止一个;-)