通过从单个 table 中选择来插入两个引用 table

Insert into two referencing tables by selecting from a single table

我的 PostgreSQL 12 数据库中有 2 个永久性 table,它们具有一对多关系(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 提取内容创建的 table一个大的 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 INSERT 语句(使用 RETURNING thing_id)来获取 thing_idthing 上的 INSERT 产生的 thing_id,但是我无法弄清楚如何从 thing 上的 INSERT 获得 both that thing_id来自 things_to_add 的原始 guid,需要进入 thing_identifier.identifier.

明确一点,thing 中唯一保证唯一的列是 thing_idthings_to_add 中唯一保证唯一的列是 id(我们不不想存储)和guid(这是我们想要的thing_identifier.identifier),所以没有办法在[=之后加入thingthings_to_add 30=] 在 thing.

您可以从 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 ();