【问题标题】:Insert into two referencing tables by selecting from a single table通过从单个表中选择插入到两个引用表中
【发布时间】:2021-12-18 22:42:45
【问题描述】:

我的 PostgreSQL 12 数据库中有 2 个具有一对多关系的永久表(thingthing_identifier)。第二个——thing_identifier——有一个引用thing的列,这样thing_identifier可以为给定的thing保存多个外部标识符:

CREATE TABLE IF NOT EXISTS thing
(
    thing_id SERIAL PRIMARY KEY,
    thing_name TEXT, --this is not necessarily unique
    thing_attribute TEXT --also not unique 
);

CREATE TABLE IF NOT EXISTS thing_identifier
(
    id SERIAL PRIMARY KEY,
    thing_id integer references thing (thing_id),
    identifier text
);

我需要在thingthing_identifier 中插入一些新数据,这两个数据都来自我使用COPY 创建的表,用于将大型CSV 文件的内容拉入数据库,类似于:

CREATE TABLE IF NOT EXISTS things_to_add
(
    id SERIAL PRIMARY KEY,
    guid TEXT, --a unique identifier used by the supplier
    thing_name TEXT, --not unique
    thing_attribute TEXT --also not unique

);

样本数据:

INSERT INTO things_to_add (guid, thing_name) VALUES 
  ('[111-22-ABC]','Thing-a-ma-jig','pretty thing'),
  ('[999-88-XYZ]','Herk-a-ma-fob','blue thing');

目标是让things_to_add 中的每一行在thingthing_identifier 中产生一个新行,如下所示:

thing:

| thing_id | thing_name          |  thing attribute  |
|----------|---------------------|-------------------|
|     1    | thing-a-ma-jig      |  pretty thing
|     2    | herk-a-ma-fob       |  blue thing

thing_identifier:

| id | thing_id | identifier       |
|----|----------|------------------|
|  8 |     1    | '[111-22-ABC]'   |
|  9 |     2    | '[999-88-XYZ]'   |

我可以使用 CTE INSERTstatement(与 RETURNING thing_id)来获得 thing_idINSERT 上的 thing 产生,但我不知道如何获得 两者thing_id 来自INSERT 上的thing 原来的guid 来自things_to_add,这需要进入thing_identifier.identifier

需要明确的是,thing 中唯一保证唯一的列是 thing_idthings_to_add 中唯一保证唯一的列是 id(我们不想存储)和 guid (这是我们在thing_identifier.identifier 中想要的),所以在thing 上的INSERT 之后没有任何方法可以加入thingthings_to_add

【问题讨论】:

    标签: postgresql sql-insert common-table-expression sql-returning


    【解决方案1】:

    您可以从 JOIN 检索 thing_to_add.guid :

    WITH list AS
    (
      INSERT INTO thing (thing_name)
      SELECT thing_name
        FROM things_to_add
      RETURNING thing_id, thing_name
    )
    INSERT INTO thing_identifier (thing_id, identifier)
    SELECT l.thing_id, t.guid
      FROM list AS l
     INNER JOIN thing_to_add AS t
        ON l.thing_name = t.thing_name
    

    那么,如果thing.thing_name 不是唯一的,那么问题就比较棘手了。从thing_to_add 上的同一个触发器更新两个表thingthing_identifier 可能会解决这个问题:

    CREATE OR REPLACE FUNCTION after_insert_thing_to_add ()
    RETURNS TRIGGER LANGUAGE sql AS
    $$
    WITH list AS
    (
      INSERT INTO thing (thing_name)
      SELECT NEW.thing_name
      RETURNING thing_id
    )
    INSERT INTO thing_identifier (thing_id, identifier)
    SELECT l.thing_id, NEW.guid
      FROM list AS l ;
    $$
    
    DROP TRIGGER IF EXISTS after_insert ON thing_to_add ;
    CREATE TRIGGER after_insert 
      AFTER INSERT
      ON thing_to_add 
      FOR EACH ROW
      EXECUTE PROCEDURE after_insert_thing_to_add ();
    

    【讨论】:

    • 这个解决方案的问题是thing.thing_name不是唯一的。如果从描述中不清楚,请道歉。在我的数据库中由thing 表示的实际表中,有很多列,但唯一保证唯一的列是thing_id。同样,things_to_add 中的 thing_name 列也不是唯一的。
    • 修改后的解决方案(非常聪明!)进行了一些调整。因为我已经在things_to_add 中有记录,所以我只是将触发器变成了一个更新触发器,并且可以执行一个简单的更新来触发它,根据需要将我的记录写入我的两个永久表。谢谢!
    猜你喜欢
    • 1970-01-01
    • 2018-05-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-10
    • 2013-11-02
    相关资源
    最近更新 更多