如何选择左外连接的最小U UID?

问题描述 投票:3回答:1

我正在尝试从表中选择一行:

  1. 有一个最小的UUID
  2. 未在另一个表中引用

但是当我尝试强制执行第一个约束时,我遇到了问题。

这里的一切按预期在整数上运行:首先,创建如下所示的表:

t1
+----+---------+
| id | content |
+----+---------+
|  1 | a       |
|  2 | b       |
|  3 | c       |
+----+---------+

t2
+----+---------+
| id | t1_id   |
+----+---------+
|  1 | 1       |
+----+---------+
postgres=# create table t1(id int, content varchar(10), primary key (id));
CREATE TABLE
postgres=# create table t2(id int, t1_id int, foreign key (t1_id) references t1(id));
CREATE TABLE
postgres=# insert into t1 values (1, 'a');
INSERT 0 1
postgres=# insert into t1 values (2, 'b');
INSERT 0 1
postgres=# insert into t1 values (3, 'c');
INSERT 0 1
postgres=# insert into t2 values (1, 1);
INSERT 0 1

现在,我想选择t1中最低的id行,它在t2中不作为外键出现。我想选择t1中有id = 2的行,它按预期工作:

postgres=# select min(t1.id) from t1 left outer join t2 on t1.id = t2.t1_id where t2.id is null;
 min
-----
   2
(1 row)

但是,当我尝试使用UUID时,最终查询无法返回任何内容。注意,我使用answer from this post来定义查找最小UUID的方法:

CREATE OR REPLACE FUNCTION min(uuid, uuid)
RETURNS uuid AS $$
BEGIN
    IF $2 IS NULL OR $1 > $2 THEN
        RETURN $2;
    END IF;

    RETURN $1;
END;
$$ LANGUAGE plpgsql;


create aggregate min(uuid) (
  sfunc = min,
  stype = uuid,
  combinefunc = min,
  parallel = safe,
  sortop = operator (<)
);

现在,构建表与以前一样,使用gen_random_uuid自动生成UUID:

postgres=# drop table t2;
postgres=# drop table t1;
postgres=# create table t1(id uuid default gen_random_uuid(), content varchar(10), primary key (id));
postgres=# create table t2(id int, t1_id uuid, foreign key (t1_id) references t1(id));
postgres=# insert into t1(content) ('a');
postgres=# insert into t1(content) values ('a');
postgres=# insert into t1(content) values ('b');
postgres=# insert into t1(content) values ('c');

我们在t1成功完成了三个参赛作品。添加一个条目到t2

postgres=# select * from t1;
                  id                  | content
--------------------------------------+---------
 b6148ae3-db56-4a4a-8d46-d5b4f04277ac | a
 03abd324-8626-4fb1-9cb0-593373abf9ca | b
 9f12b297-3f60-48a7-8282-e27c3aff1152 | c
(3 rows)


postgres=# insert into t2 values(1, '9f12b297-3f60-48a7-8282-e27c3aff1152');

尝试选择t1中的行,其中最小ID不会出现在t2中,请注意这是失败的。

postgres=# select min(t1.id) from t1 left outer join t2 on t1.id = t2.t1_id where t2.id is null;
 min
-----

(1 row)

这里我们展示我们可以在t1中选择两个未引用的条目,我们可以独立选择最小的UUID:

postgres=# select t1.id from t1 left outer join t2 on t1.id = t2.t1_id where t2.id is null;
                  id
--------------------------------------
 03abd324-8626-4fb1-9cb0-593373abf9ca
 b6148ae3-db56-4a4a-8d46-d5b4f04277ac
(2 rows)

postgres=# select min(id) from t1;
                 min
--------------------------------------
 03abd324-8626-4fb1-9cb0-593373abf9ca
(1 row)

因此,当我尝试选择最小UUID同时尝试执行左外连接时,会发生一些有趣的事情。

编辑:使用not exists时存在同样的问题:

postgres=# select min(id) from t1 where not exists (select t1_id from t2 where t2.t1_id = t1.id);
 min
-----

(1 row)

但使用not in时不会出现问题:

postgres=# select min(id) from t1 where id not in (select t1_id from t2);
                 min
--------------------------------------
 03abd324-8626-4fb1-9cb0-593373abf9ca
(1 row)
postgresql join aggregate-functions uuid
1个回答
2
投票

找到一个解决方案,结果比较来自this post的UUID的功能是不正确的。这是我写的函数,它可能性能较差,它传递了之前失败的所有情况:

    CREATE FUNCTION min_uuid(uuid, uuid)
    RETURNS uuid AS $$
    BEGIN
        -- if they're both null, return null
        IF $2 IS NULL AND $1 IS NULL THEN
            RETURN NULL ;
        END IF;

        -- if just 1 is null, return the other
        IF $2 IS NULL THEN
            RETURN $1;
        END IF ;
        IF $1 IS NULL THEN
            RETURN $2;
          END IF;

        -- neither are null, return the smaller one
        IF $1 > $2 THEN
            RETURN $2;
        END IF;

        RETURN $1;
    END;
    $$ LANGUAGE plpgsql;


    create aggregate min(uuid) (
      sfunc = min_uuid,
      stype = uuid,
      combinefunc = min_uuid,
      parallel = safe,
      sortop = operator (<)
    );
© www.soinside.com 2019 - 2024. All rights reserved.