Oracle KEEP DENSE_RANK FIRST 的 Postgres 转换

Postgres Conversion of ORacle KEEP DENSE_RANK FIRST

将特定系统从 Oracle 转换为 Postgres,我在其中使用 Oracle KEEP dense rank first 函数。在那个系统中(出于各种原因)更新语句必须像这样格式化:

UPDATE aaa a
   SET a.dataval =
           (SELECT MAX (b.data_val) KEEP (DENSE_RANK FIRST ORDER BY b.sort_val)
              FROM bbb b
             WHERE a.id = b.aaa_id);

或者为了测试,我们可以将其编写为 Oracle 的内联视图样式:

SELECT id,
       (SELECT MAX (b.data_val) KEEP (DENSE_RANK FIRST ORDER BY b.sort_val)
          FROM bbb b
         WHERE a.id = b.aaa_id)
  FROM aaa a;

所以我在 Postgres 中尝试了类似的构造,以及许多其他尝试 window 和行范围等

select  id,
    (select first_value(b.data_val) over (order by b.sort_val)
       from bbb b
      where a.id = b.aaa_id )
from aaa a;

我得到了错误: SQL Error [21000]: ERROR: more than one row returned by a subquery used as an expression.

我原以为 aaa 的每一行都会 return 来自 bbb table 的一行。但这似乎并没有发生。有没有办法以这种方式为 Postgres 格式化查询?非常感谢 Postrges 新手的任何帮助。

这是在 Oracle 中创建测试对象的脚本。

CREATE TABLE aaa (
    id integer NOT NULL,
    dataval integer NULL
);

CREATE TABLE bbb (
    aaa_id integer NOT NULL,
    bbb_id integer NOT NULL,
    sort_val integer NOT NULL,
    data_val integer NOT NULL
);

INSERT INTO aaa (id,dataval) VALUES (1,NULL);
INSERT INTO aaa (id,dataval) VALUES (2,NULL);
INSERT INTO aaa (id,dataval) VALUES (3,NULL);

INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (1,1,3,10);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (1,2,2,11);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (1,3,1,12);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (2,4,6,13);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (2,5,5,14);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (2,6,4,15);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (3,7,9,16);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (3,8,8,17);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES     (3,9,7,18);

--Query
SELECT id,
       (SELECT MAX (b.data_val) KEEP (DENSE_RANK FIRST ORDER BY b.sort_val)
          FROM bbb b
         WHERE a.id = b.aaa_id)
  FROM aaa a;

/*expected Result
ID                                     DATAVAL                                 
1                                      12                                      
2                                      15                                      
3                                      18                                      
*/

这是 Postgres 的相同测试对象设置:

CREATE TABLE aaa (
    id int8 NOT NULL,
    dataval int8 NULL
);
CREATE UNIQUE INDEX aaa_id_idx ON aaa (id);

CREATE TABLE bbb (
    aaa_id int8 NOT NULL,
    bbb_id int8 NOT NULL,
    sort_val int8 NOT NULL,
    data_val int8 NOT NULL
);
CREATE UNIQUE INDEX bbb_aaa_id_idx ON bbb on (aaa_id, bbb_id);

INSERT INTO aaa (id,dataval) VALUES
     (1,NULL),
     (2,NULL),
     (3,NULL);
INSERT INTO bbb (aaa_id,bbb_id,sort_val,data_val) VALUES
     (1,1,3,10),
     (1,2,2,11),
     (1,3,1,12),
     (2,4,6,13),
     (2,5,5,14),
     (2,6,4,15),
     (3,7,9,16),
     (3,8,8,17),
     (3,9,7,18);


谢谢!

您可以使用带有行限制子句的子查询或横向连接

select a.id, b.data_val
from aaa a
left join lateral (
    select b.data_val
    from bbb b
    where b.aaa_id = a.id
    order by b.sort_val
    limit 1
) b on true

另一个选择是 distinct on:

select distinct on (a.id) a.id, b.data_val
from aaa a
left join bbb b on b.aaa_id = a.id
order by a.id, b.sort_val

Demo on DB Fiddle - 两个查询都产生:

id | data_val
-: | -------:
 1 |       12
 2 |       15
 3 |       18

如果你想要更新:

update aaa a
set data_val = (
    select b.data_val
    from bbb b
    where b.aaa_id = a.id
    order by sort_val
    limit 1
)