使用CTE INSERT的结果进行插入以提供唯一的ID值


8

我正在写一份工作,将数据从旧设计转换为新设计。在此过程中,我需要从插入中获取ID到单独的表中,并在插入目标表中使用它,如下所示:

CREATE TABLE t1 {
  t1_id BIGSERIAL,
  col1 VARCHAR
};
CREATE TABLE t2 {
  t2_id BIGSERIAL,
  col2 VARCHAR, -- renamed from col1 to avoid confusion
  t1_id BIGINT REFERENCES t1.t1_id
};

我定义了与以下形式匹配的SQL:

WITH ins AS (
  INSERT INTO t1 (t1_id) VALUES (DEFAULT) RETURNING t1_id
) INSERT INTO t2
  (col1, t1_id)
SELECT
  a.val1, (SELECT * FROM ins)
FROM t3 a;

我希望它对..的SELECT * FROM ins每一行都运行,SELECT但是相反,它只运行一次,并将该值用于SELECT。如何重组我的SQL以获得所需的行为?

编辑4

t1最终看起来像:

1,<NULL>
(1 row)

t2最终看起来像:

10,'a',1
11,'b',1 -- problem with id from t1 being 1
12,'c',1 -- problem with id from t1 being 1
.
.

我希望t1看起来像什么:

1,<NULL>
2,<NULL>
3,<NULL>
.
.

我希望t2看起来像什么:

10,'a',1
11,'b',2 -- id from t1 of 2
12,'c',3 -- id from t1 of 3
.
.

编辑 为了解决a_horse_with_no_name所说的内容,我也尝试了此操作(结果相同):

WITH ins AS (
  INSERT INTO t1 (t1_id) VALUES (DEFAULT) RETURNING t1_id
) INSERT INTO t2
  (col1, t1_id)
SELECT
  a.val1, b.t1_id
FROM t3 a
JOIN ins b ON TRUE;

edit2 我只是尝试直接SEQUENCE在查询中引用适当的方法,并且确实可以工作-但我根本不喜欢该解决方案(主要是因为我不喜欢对对象名称进行硬编码。)而不是直接引用SEQUENCEI 的名称,我将不胜感激。:)

EDIT3 我想另一个解决办法是使用的PROCEDUREINSERT,而不是一个CTE ..但我还是欣赏选项/建议。


1
您需要加入inst3
a_horse_with_no_name 2014年

我也尝试过,它仍然只计算一次值。但是也许我的加入并不十分正确。我将编辑我的帖子以显示我尝试过的操作。
Joishi Bodio

1
您只向其中插入一行t1,而不为提供任何值t1.col1。该列的数据应该从哪里来?被t1.col1有关t2.col1
ypercubeᵀᴹ

ypercube-t1.col1允许为NULL,并将在以后的过程中插入。因为我在实际的行值中将CTE引用为SUBSELECT,所以我认为它会被执行多次-但是事实证明我在这种假设下是不正确的。这就是为什么我在这里提出这个问题。在过去的几个小时中,我已经尝试过在google上寻找答案,但是还无法找到正确的答案。不,t1.col1与t2.col1不相关..对此感到抱歉。
Joishi Bodio

1
仍然INSERT INTO t1 (t1_id) VALUES (DEFAULT)只将1行插入t1。因此,它并不重要,如果你把ins对的FROM条款,并将其加入到t3与否。您能告诉我们如何将2行(或更多行)插入t1吗?更重要的是,您如何知道2个(或更多)t1.id值中的哪个将与插入的行匹配t2
ypercubeᵀᴹ

Answers:


8

我不明白为什么如果只有1-1关系,就需要2个表。但这是(pk是的主键t3):

WITH ins AS (
  INSERT INTO t1 (col1) 
    SELECT NULL FROM t3 
  RETURNING t1_id
) 
, r AS
( SELECT t1_id, ROW_NUMBER() OVER () AS rn
  FROM ins
) 
, t AS
( SELECT *, ROW_NUMBER() OVER () AS rn
  FROM t3
) 
INSERT INTO t2
  (col1, t1_id)
SELECT
  t.val1, r.t1_id
FROM t 
  JOIN r USING (rn) ;

如果您的t3是SELECT而不是预先存在的表的结果,则可以将其实现为这样,从而不必重复两次t3查询:

WITH t3 AS (
  SELECT ...
), ins AS (
  INSERT INTO t1 (col1)
    SELECT NULL FROM t3
  RETURNING t1_id
), r AS (
  SELECT t1_id, ROW_NUMBER() OVER () AS rn
  FROM ins
), t AS (
  SELECT *, ROW_NUMBER() OVER () AS rn
  FROM t3
) INSERT INTO t2
  (col1, t1_id)
SELECT
  t.val1, r.t1_id
FROM t 
  JOIN r USING (rn);

之所以需要两个表,是因为还有另一个表也需要在t1中存储值。(t1将同时具有指向t2和t4的链接)t1旨在成为联系信息表(使用fkeys地址,电子邮件和电话号码表)以及t2和t4都是不同域中的实体,需要与之关联的联系信息。.我的某些词汇可能不正确,但这实际上就是原因。谢谢您的回答-我将进行测试。
Joishi Bodio

编辑了一个小错误。使用最新版本。
ypercubeᵀᴹ

好,那很有道理。但是您可能根本不需要t2_id。似乎您可以将t2(t1_id)用作的PK t2
ypercubeᵀᴹ

:)目前,这给了我DEFAULT的语法错误-试图找出它可能是什么。ERROR: syntax error at or near "DEFAULT" LINE 2: DEFAULT AS contact_detail_id
Joishi Bodio

嗯,看来DEFAULT不能用这种方式。也返回t.pk
ypercubeᵀᴹ
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.