插入一个表中首先自动插入其他表(PostgreSQL)

时间:2015-12-10 16:00:34

标签: postgresql triggers

我在复杂模式中有很多表,所有表都按照这样的方式生成:

CREATE TABLE first_id (
    nid BIGSERIAL PRIMARY KEY
);

CREATE TABLE first (
    nid BIGINT REFERENCES example_id(nid),
    revisions TSRANGE NOT NULL,
    column1 TEXT
);

CREATE TABLE second_id (
    nid BIGSERIAL PRIMARY KEY
);

CREATE TABLE second (
    nid BIGINT REFERENCES second_id(nid),
    revisions TSRANGE NOT NULL,
    column2 BIGINT REFERENCES first_id(nid),
    column3 TEXT
);

不同的表除了nid和修订版之外还有不同的列,它们都有。

这种设计的原因是我们对同一行保持不同的修订(通过非重叠的TSRANGE),但希望不同类型之间的引用在抽象ID之间:s,而不是在特定修订之间(我们在问题时间)。

当我们想要创建新的first行时,我们首先

INSERT INTO first_id DEFAULT VALUES RETURNING nid

然后我们将此ID提取到调用代码中,并在新查询中再次发出它:

INSERT INTO first (nid, revisions, column1) 
  VALUES ([nid from previous query], TSRANGE(NOW()::TIMESTAMP, 'INFINITY'), 'A new string');

当我们逐个创建新ID时,这一切都很好。但是我们现在想要从另一个查询批量创建一次成千上万的ID。通常我们会做一些像

这样的事情
INSERT INTO first (nid, revisions, column1)
  SELECT ???, TSRANGE(NOW()::TIMESTAMP, 'INFINITY'), somecolumn
    FROM sometable
   WHERE someexpression

当然,这不起作用 - 如果我们插入234行,我们需要在firstname_id中插入234个。我们可以使用带有多个CTE的查询:s插入id然后在row_number或类似物上关联,但它变得笨重且难以理解。

相反,我们需要RULETRIGGER或类似功能,其工作原理如下:

When inserting into first:
  For each new row:
    If <nid> is NULL:
      INSERT INTO first_id, keeping the new <nid>
      Replace the new row's <nid> with the value from the previous step
    Else:
      Keep the new row as is
    Insert the new row into first

即使我们运行产生成千上万个新ID的INSERT ... SELECT ...,我们也希望它能有效地工作,当然,即使多个事务同时进行,也能正常工作。不幸的是,我还没有足够强大的PostgreSQL力量知道什么是最好的,也不知道它是如何工作的。感谢任何和所有输入。

1 个答案:

答案 0 :(得分:2)

您可以在表格中创建触发器,如下所示:

CREATE OR REPLACE FUNCTION f_t_first()
  RETURNS trigger AS
$BODY$
BEGIN
  if (NEW.NID IS NULL) then
    INSERT INTO first_id DEFAULT VALUES RETURNING nid INTO NEW.NID;  
  end if;
  RETURN NEW;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE;


CREATE TRIGGER t_first
  BEFORE INSERT
  ON first
  FOR EACH ROW
  EXECUTE PROCEDURE f_t_first();

然后只需插入“第一”表而不用担心“nid”计算和“first_id”人口。它将由触发器本身完成。

INSERT INTO first (revisions, column1)
  SELECT TSRANGE(NOW()::TIMESTAMP, 'INFINITY'), somecolumn
    FROM sometable
   WHERE someexpression