使用PostgreSQL 16.2,我有两个表:

task_pool张桌子

create table task_pool (
  task_id serial primary key
, order_id integer
, user_id integer
, expire_time timestamp
, status integer default 0
);

样本数据:

task_id order_id user_id expire_time status
50 80 1 2024-03-00 11:52:44 -1(expired)
51 81 1 2024-03-00 16:00:44 -1 (expired)
52 80 2 2024-04-19 12:31:22 0 (active)
53 81 4 2024-03-00 16:52:44 -1(expired)
54 81 6 2024-03-00 16:51:44 -1(expired)

reactivate_taskthis table is actually a temporary table using WITH AS in a longer query, but for simplicity here is code to create the table:

create table reactivate_task(
, order_id integer
, successfully_added
, total_amount_requested
, amount_to_reactivate integer
);

样本数据:

order_id successfully_added total_amount_requested amount_to_reactivate
80 0 3 3
81 0 3 2

我try 做的是,对于reactivate_task中的每一行,更新x个最旧的过期任务,其中order_id是该行的amount_to_reactivate

因此,在上面的示例表中,查看reactivate_task表,3个最旧的、order_id为80的过期任务应该被"重新激活".另外,order_id为81的2个最旧的过期_任务应该被"重新激活".

task_pool列出了预期结果:

task_id order_id user_id expire_time status
50 80 1 2024-03-21 12:00:00 0(active) --status set to 0 and expire_time to current_timestamp
51 81 1 2024-03-21 12:00:00 0(active) ----status set to 0 and expire_time to current_timestamp
52 80 2 2024-04-19 12:31:22 0 (active) --NOT updated (even though amount_to_reactive was 3 for order_id 80, there was only ONE expired task (task_id 50) that was expired.
53 81 4 2024-03-19 16:52:44 -1(expired) --NOT updated (amount_to_reactive was only 2 for order 81, so only the 2 rows with oldest expire_time were updated (task_id 50 & 54)
54 81 6 2024-03-21 12:00:00 0 (active) ----status set to 0 and expire_time to current_timestamp

这是一个查询一行的示例:

UPDATE task_pool 
SET status=0, 
    expire_time = current_timestamp + (2 * interval '1 day')
WHERE task_id IN (
    SELECT task_id
    FROM task_pool
    WHERE order_id = *--order_id from CURRENT ROW OF REACTIVATE_TASK--*
          AND status=-1 
    ORDER BY expire_time
    LIMIT *--amount_to_reactivate from current row of reactivate_task--*

但是使用这个查询,我必须遍历Postgr以外的reactivate_task行(我使用node和node—postgr,在results.rows[]上使用for循环)

在Postgres中,有没有一种方法可以在一个查询中做到这一点,而不是迭代每一行?

推荐答案

UPDATE task_pool t
SET    status = 0
     , expire_time = current_timestamp + (interval '2 days')
FROM   reactivate_task r
CROSS  JOIN LATERAL (
   SELECT t1.task_id
   FROM   task_pool t1
   WHERE  t1.order_id = r.order_id
   AND    t1.status = -1
   ORDER  BY t1.expire_time
   LIMIT  r.amount_to_reactivate
   ) t1
WHERE t.task_id = t1.task_id;

fiddle

UPDATE syntax in the manual分的基本要求.

确定LATERAL个子查询中候选行的最大数量.参见:

FROM子句中与更新的附加关系不能横向连接到目标表,因此在这种特殊情况下,您必须使用同一表的第二个实例.相关内容:

CROSS JOINreactivate_task中删除了找不到要激活的合格行的行.

如果amount_to_reactivate是null(它可能永远不应该是null),实际上没有限制.

如果task_pool中的(order_id, expire_time)不是UNIQUE,则领带将被任意打破,除非你在ORDER BY中添加更多的表达式以使其确定性.

如果表task_pool上可能存在并发的、竞争的写入,则可能需要添加一个锁定子句.但不是在同一个子查询中,您需要在具有LIMIT的子查询级别之上添加另一个子查询级别.相关内容:

Sql相关问答推荐

表名数组

Postgres:对包含数字的字符串列表进行排序

如何根据同一表中某一列中的值重新排列行(仅输出它们)(重新排序)?

在SQL Server中设置关联对象的有效JSON格式

将一个数组反嵌套到另外两个数组SQL中(Athena/presto)

在SQL中将项分配给容器

如何使用jsonn_populate_record()插入到包含IDENTITY列的表中

如何在MS Access中基于另外两个表自动填充一个表中的字段?

SQL中相同表内的VLOOKUP等价

将 jsonb 数组中的对象取消嵌套到单独的行中

根据时间值提取记录

从每月生成的系列中生成每日汇率

使用长 IN 子句的 SQL 优化

基于变量的条件 WHERE 子句

编写查询以根据级别 (p2) 返回父位置

如何按日期和位置对最近 3 个报告日期的 SQL 查询结果进行透视?

过滤具有一对多关系的两个表之间的数据

SELECT 用于 Parent、Children 和 ORDER BY [Order] 列

使用一组值进行分组和计数

Snowflake SQL group-by 的行为不同,具体取决于列是按位置引用还是按别名引用