我正在try 编写一个PostgreSQL函数,该函数根据指定的条件复制表的所有行,并返回现有行ID和复制的行ID的映射.

以下是我要使用的示例数据.

CREATE TABLE foo (
  id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY (START WITH 6),
  foo_content text NOT NULL,
  condition_col integer NOT  NULL
);

INSERT INTO foo OVERRIDING SYSTEM VALUE VALUES
(  1 , 'Sing, O goddess', 99),
(  2 , 'the anger of Achilles', 99),
(  3 , 'son of Peleus', 99),
(  4 , 'that brought countless ills', 2),
(  5 , 'upon the Achaeans', 3);

SELECT * FROM foo;

这是我try 编写能够复制条目的SQL函数:

CREATE OR REPLACE FUNCTION duplicate_entries(condition INT, new_condition INT)
RETURNS TABLE (
        old_id INT,
        new_id INT) AS
$$
BEGIN
    RETURN QUERY (
        WITH select_cte AS (
            SELECT id
            FROM foo f
            WHERE f.condition_col = condition
        ), insertion_cte AS (
            INSERT INTO foo (foo_content, condition_col)
            SELECT f.foo_content, new_condition
            FROM select_cte s
            JOIN foo f ON s.id = f.id
            RETURNING id
        )
        --- how to I return a mapping of ids from here?
    );
END;
$$ LANGUAGE plpgsql;

-- To call the function and get the result
SELECT * FROM duplicate_entries(99, 100);

-- To see the updated 'foo' table
SELECT * FROM foo;

在这里,虽然我已经实现了期望的函数,但我无法从该函数返回old_id和new_id的映射.

返回的表应该是这样的.我曾try 使用Union all查询,但似乎没有起到作用.我认为临时表在这种情况下可能会有所帮助,但我不想使用临时表,因为这可能是一种糟糕的做法.

下面是我想要的输出:

old_ids new_ids
1 6
2 7
3 8

推荐答案

您可以(但可能不应该)使用rely on the order of inserted rows来匹配 Select 和RETURNING子句.更好的方法是在插入之前生成新的ID,使用the sequence that is underlying the identity column:

CREATE OR REPLACE FUNCTION duplicate_entries(condition INT, new_condition INT)
RETURNS TABLE (old_id INT, new_id INT)
LANGUAGE SQL
BEGIN ATOMIC
  WITH mapping AS (
    SELECT id AS old_id, nextval(pg_get_serial_sequence('foo', 'id')) AS new_id
    FROM foo f
    WHERE f.condition_col = condition
  ), __insertion AS (
    INSERT INTO foo (id, foo_content, condition_col)
    OVERRIDING SYSTEM VALUE
    SELECT m.new_id, f.foo_content, new_condition
    FROM mapping m
    JOIN foo f ON m.old_id = f.id
  )
  TABLE mapping;
END;

(online demo)

但是,这确实需要对基础序列具有USAGE权限,即使角色可以正常使用IDENTITY列,默认情况下也可能不会授予该权限.您可能需要在函数上使用SECURITY DEFINER.

Postgresql相关问答推荐

Postgresql函数返回值别名

Postgs SQL用于比较两个表之间的数据并填充到目标中

PostgreSQL:`row_alias为null`且`row_alias不是null`返回值不一致

Postgres 使用不同元素数据类型的订单数据

有没有办法共享数据或监控复杂 PostgresQL 事务的进度?

Postgres 将分区附加到表的时间太长.想明白为什么

Postgres 转储按顺序插入

全文搜索(Postgres)与Elastic search

postgresql中的外键可以被触发器违反

如何在 PostgreSQL 触发器函数中获取表名?

Docker - Postgres 和 pgAdmin 4:Connection refused

在 postgreSQL 中更改序列名称

如何使用 psql 命令列出、创建、使用和判断数据库?

如何为查询执行设置语句超时

Postgres jsonbNOT contains运算符

如何在 plpgsql 中使用记录类型变量?

PostgreSQL 表变量

防止 PostgreSQL 中的递归触发器

判断 PostgreSQL 中的角色是否设置了密码

如何在 postgres 查询中排名