【问题标题】:How to get child table's foreign key to have same value as parent's primary auto-increment key如何让子表外键具有与父主自动增量键相同的值
【发布时间】:2017-08-29 03:11:20
【问题描述】:

我已经创建了这两个表:

CREATE TABLE Purchase(
purchaseID SERIAL,
custName VARCHAR(30) NOT null,
PRIMARY KEY (purchaseID));

CREATE TABLE PurchasedItem(
purchaseID INT,
itemNo INT NOT NULL,
PRIMARY KEY (purchaseID, itemNo),
FOREIGN KEY (purchaseID) REFERENCES Purchase(purchaseID));

接下来我希望将数据插入到两个表中,购买项目的purchaseID外键与Purchase表中的purchaseID Serial具有相同的值。

我正在使用一个名为 PSequel 的 PostgreSQL 客户端。我尝试在客户端中首先将 AUTOCOMMIT 设置为关闭,这样我就可以在同一个事务中使用两个 INSERT 语句,但是客户端无法识别“自动提交”,所以我在终端中尝试了它,我认为它有效......无论如何,这是我试过的两个 INSERT 语句。

INSERT INTO Purchase(custName) VALUES ('Lendl');
INSERT INTO PurchasedItem(purchaseID, itemNo) VALUES (DEFAULT, 111);
commit;

但是我得到一个错误:

ERROR: null value in column purchaseID violates not-null constraint.

这是指PurchableItem 的purchaseID,因为它在运行第一个INSERT 语句时它本身可以工作。我该如何解决这个问题?

【问题讨论】:

    标签: postgresql


    【解决方案1】:

    DEFAULT 将适用于SERIAL,因为它为列设置默认值。所以

    INSERT INTO Purchase VALUES (DEFAULT,'Lendl');
    

    应该可以。但是PurchasedItem.purchaseID没有设置默认值,所以它尝试插入NULL(并且null还没有在引用的列中),所以它失败了。

    尝试:

    INSERT INTO Purchase(custName) VALUES ('Lendl') RETURNING purchaseID;
    

    您将看到插入的purchaseID 的值,在下一个查询中使用它:

    INSERT INTO PurchasedItem(purchaseID, itemNo) VALUES (_the_value_above_, 111);
    commit;
    

    如果您希望它在没有交互性的情况下使用,请使用 DO 块和 returning purchaseID into _value

    更新

    or cte, smth like

    WITH i AS (
      INSERT INTO Purchase(custName, orderedDate) 
      VALUES ('Lendl', '2016-09-28') 
      RETURNING purchaseID
    )
    insert into PurchasedItem
    select i.purchaseID,'smth',3
    from i
    

    【讨论】:

    • 如何访问 the_value_above 作为 purchaseID?顺便说一句,我在 postgres 客户端中运行它:psequel。不确定如何在下一条语句中使用返回的 purchaseid。
    • 我也不明白最后一部分,即 DO 块。它是如何工作的?
    • 返回 purchaseID 会显示您选择的值,所以只需复制粘贴即可
    【解决方案2】:

    你可以使用lastval()

    INSERT INTO Purchase(custName) VALUES ('Lendl');
    INSERT INTO PurchasedItem(purchaseID, itemNo) VALUES (lastval(), 111);
    commit;
    

    或者直接查询底层序列:

    INSERT INTO Purchase(custName) VALUES ('Lendl');
    INSERT INTO PurchasedItem(purchaseID, itemNo) 
    VALUES (currval('purchase_purchaseid_seq'), 111);
    commit;
    

    或者如果不想依赖序列的自动命名,使用pg_get_serial_sequence获取列关联的序列:

    INSERT INTO Purchase(custName) VALUES ('Lendl');
    INSERT INTO PurchasedItem(purchaseID, itemNo) 
    VALUES (currval(pg_get_serial_sequence('purchase', 'purchaseid')), 111);
    commit;
    

    更多详情见手册:https://www.postgresql.org/docs/current/static/functions-sequence.html

    【讨论】:

    • 这些都可以完美运行 :) 使用其中一个有什么好处吗?
    • @IvanLendl lastval() 只有在两个插入之间的事务中没有发生任何其他事情时才会起作用。否则,它们三个都一样 - 这是个人品味的问题
    • 如果我想放入一个不是序列的属性怎么办?那么我如何将这个值从一张表传递到另一张表呢?
    • @IvanLendl:见herehereherehere
    • CTE 是唯一能做到这一点的手表吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-15
    • 2019-06-26
    • 1970-01-01
    • 1970-01-01
    • 2014-04-21
    • 1970-01-01
    相关资源
    最近更新 更多