2016-08-24 70 views
1

我试图为电话簿开发一个简单的数据库。这是我写的:将数据插入强归一化的数据库并保持完整性(Postgres)

CREATE TABLE phone 
(
    phone_id SERIAL PRIMARY KEY, 
    phone CHAR(15), 
    sub_id INT, -- subscriber id -- 
    cat_id INT -- category id -- 
); 

CREATE TABLE category 
(
    cat_id SERIAL PRIMARY KEY, -- category id -- 
    cat_name CHAR(15)  -- category name -- 
); 

CREATE TABLE subscriber 
(
    sub_id SERIAL PRIMARY KEY, 
    name CHAR(20), 
    fname CHAR(20), -- first name -- 
    lname CHAR(20), -- last name -- 
); 

CREATE TABLE address 
(
    addr_id  SERIAL PRIMARY KEY, 
    country  CHAR(20), 
    city   CHAR(20), 
    street  CHAR(20), 
    house_num  INT, 
    apartment_num INT 
); 

-- many-to-many relation -- 
CREATE TABLE sub_link 
(
    sub_id INT REFERENCES subscriber(sub_id), 
    addr_id INT 
); 

我创建了一个链接表许多一对多的关系,因为没有几个人能住在同一个地址和一个人可以生活在不同的时间不同的地点。

但我无法弄清楚如何在强标准化DB这样的添加数据和维护数据的完整性。

第一个改进是,我加入inique键上的地址表bacause此表不应该包含重复的数据:

CREATE TABLE address 
(
    addr_id  SERIAL PRIMARY KEY, 
    country  CHAR(20), 
    city   CHAR(20), 
    street  CHAR(20), 
    house_num  INT, 
    apartment_num INT, 
    UNIQUE (country, city, street, house_num, apartment_num) 
); 

现在的问题是如何增加对一些人到数据库的新纪录。我想我应该用行动下一顺序:

  1. 插入记录subscriber表,因为sub_linkphone表必须使用新的用户的ID。

  2. 将表中的记录插入address表中,因为在将记录添加到sub_link之前必须存在addr_id

  3. subscriberaddress的最后记录链接到sub_link表中。但是在这一步我有一个新问题:如何在PostgreSQL中有效地从步骤1)和步骤2)获得sub_idaddr_id

  4. 然后,我需要插入一条记录到phone表。至于第3步,我不知道如何有效地从以前的查询中获得sub_id

我读了PostgreSQL里的WITH块,但是我不知道如何在我的情况下使用它。

UPDATE 我喜欢做建议:

-- First record -- 
WITH t0 AS (
    WITH t1 AS (
      INSERT INTO subscriber 
      VALUES(DEFAULT, 'Twilight Sparkle', NULL, NULL) 
      RETURNING sub_id 
    ), 
    t2 AS (
      INSERT INTO address 
      VALUES(DEFAULT, 'Equestria', 'Ponyville', NULL, NULL, NULL) 
      RETURNING addr_id 
    ) 
    INSERT INTO sub_link 
    VALUES((SELECT sub_id FROM t1), (SELECT addr_id FROM t2)) 
) 
INSERT INTO phone 
VALUES (DEFAULT, '000000', (SELECT sub_id FROM t1), 1); 

但我有一个错误:包含一个数据修改语句必须是在顶层 线路2条:为T1 AS(INSERT INTO subscriber VALUES(DEFAULT,

回答

0

)你有这个想法,插入最顶层表中的数据,以便在插入对它们的引用之前就有它们的ID

在PostgreSQL可以使用INSERT/UPDATE ... RETURNING id结构。如果你不使用自动执行的一些ORM,这可能是有用的。

唯一这里的事情是,在步骤2中,你可能要检查,如果该地址已经存在插入前:

SELECT addr_id FROM address WHERE country = ? AND city = ? ... 
1

您可以使用带有一个returning子句块做这一切在一个查询。请参阅PostgreSQL docs on INSERT。例如:

WITH t1 AS (INSERT INTO subscriber VALUES ... RETURNING sub_id), 
t2 AS (INSERT INTO address VALUES ... RETURNING addr_id) 
INSERT INTO sub_link VALUES ((SELECT sub_id FROM t1), (SELECT addr_id FROM t2)) 

注意插入单行到每个表时,这种简单的形式才有效。

这有点偏离了你的问题的主题,但我建议你也考虑在电话表外键(使用REFERENCES)中创建sub_id和cat_id列。

+0

Postgres允许在WITH块中执行两个排序插入吗?像这样:INSERT INTO sub_link VALUES((SELECT sub_id FROM t1),(SELECT addr_id FROM t2)),INSERT INTO phone VALUES(DEFAULT,“1111111”,(SELECT sub_id FROM t1),...' – memset

+0

当然,只需将除最后一个INSERT之外的所有内容都放入它自己的WITH块中,每个WITH块都可以引用之前出现的别名,例如'... t3 AS(INSERT INTO sub_link VALUES((SELECT ... FROM t1),... ))INSERT INTO phone ...' – ASL

+0

更新的原始帖子。查询变得非常复杂。使用嵌套的WITH'块,我得到一个错误'WITH子句包含一个数据修改语句必须在顶部' – memset