将数据插入强规范化数据库并保持完整性(Postgres)

时间:2016-08-24 16:35:31

标签: postgresql postgresql-9.3 postgresql-9.4

我正在尝试为电话簿开发一个简单的数据库。这就是我写的:

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
);

我创建了一个多对多关系的链接表,因为很少有人可以住在同一个地址,一个人可以在不同的时间住在不同的地方。

但我无法弄清楚如何在强规范化的数据库中添加数据,并保持数据的完整性。

第一个改进是我在地址表上添加了inique键,因为这个表不应该包含重复的数据:

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表格,因为在将记录添加到addr_id之前必须存在sub_link

  3. 关联subscriber表格中addresssub_link的最后记录。但是在这一步我遇到了一个新问题:如何有效地从PostgreSQL中的步骤1)和2)获得sub_idaddr_id

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

  5. 我在Postgres中读到了WITH块,但在我的情况下我无法弄清楚如何使用它。

    更新 我完成了ASL建议:

    -- 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);
    

    但是我有一个错误:包含数据修改语句的WITH子句必须位于顶层 第2行:使用t1 AS(插入订户价值(默认,

2 个答案:

答案 0 :(得分:1)

您可以使用带有RETURNING子句的WITH块在一个查询中完成所有操作。见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))

请注意,只有在每个表中插入一行时,此简单表单才有效。

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

答案 1 :(得分:0)

你明白了。从最顶层的表中插入数据,以便在插入对它们的引用之前获得它们的ID。

在PostgreSQL中,您可以使用INSERT/UPDATE ... RETURNING id构造。如果您没有使用某些自动执行此操作的ORM,则可能会有用。

这里唯一的一点是,在第2步中你可能想要在插入之前检查地址是否已经存在:

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