postgresql 中不存在函数 min(uuid)

Posted

技术标签:

【中文标题】postgresql 中不存在函数 min(uuid)【英文标题】:Function min(uuid) does not exist in postgresql 【发布时间】:2018-03-12 03:00:06 【问题描述】:

我已经使用 sqoop 将表从 Postgres 导入到 hdfs。我的表有 uuid 字段作为主键,我的命令 sqoop 如下:

sqoop import --connect 'jdbc:postgresql://localhost:5432/mydb' --username postgreuser --password 123456abcA --driver org.postgresql.Driver --table users --map-column-java id=String --target-dir /hdfs/postgre/users --as-avrodatafile --compress -m 2

但我得到了错误:

Import failed: java.io.IOException: org.postgresql.util.PSQLException: ERROR: function min(uuid) does not exist

我尝试执行 sql 命令:SELECT min(id) from users 并得到同样的错误。我该如何解决?我使用 Postgres 9.4、hadoop 2.9.0 和 sqoop 1.4.7

【问题讨论】:

您可以将uuid 列转换为文本:例如:min(id::text) 感谢@a_horse_with_no_name 的回答,但我怎么能在 sqoop 中投射 uuid。我尝试了 --map-column-java id=String ,但没有用。 【参考方案1】:

我想感谢@robin-salih 的回答,我已经使用它和 min for int 的实现来构建以下代码:

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 (<)
);

几乎相同,但利用了 B-tree 索引,因此 select min(id) from tbl 工作在几毫秒内。

P.S.我不是 pgsql 专家,也许我的代码有问题,在生产中使用前请仔细检查,但我希望它正确使用索引和并行执行。我只是从示例代码中完成的,而不是深入研究 PG 中聚合背后的理论。

【讨论】:

我发现这是不正确的:***.com/a/55268461/1489726 是的,有时它不应该返回NULL 哦,该死的 SQL 的 null 威胁。很好,你发现了。【参考方案2】:

Postgres 没有内置的 min/max 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
);

【讨论】:

【参考方案3】:

我发现@robin-salih 和@bodgan-mart 提供的答案是一个很好的起点,但最终是不正确的。这是一个对我来说效果更好的解决方案:

    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 (<)
    );

更多详情请看我How to select minimum UUID with left outer join?的帖子

【讨论】:

【参考方案4】:

我正在为使用 least/greatest 的 uuid 定义 min/max 聚合,我认为这应该提供最佳性能,因为它们是 postgres 原生的(但我还没有对其进行基准测试)。

由于least/greatest 是特殊形式(据我所知),我必须使用我标记为不可变且并行安全的函数来代理它们。

least/greatest 已经有适当的空值处理行为。

我在 Postgres 13 的生产环境中使用这些。

create or replace function min(uuid, uuid)
    returns uuid
    immutable parallel safe
    language plpgsql as
$$
begin
    return least($1, $2);
end
$$;

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

create or replace function max(uuid, uuid)
    returns uuid
    immutable parallel safe
    language plpgsql as
$$
begin
    return greatest($1, $2);
end
$$;

create aggregate max(uuid) (
    sfunc = max,
    stype = uuid,
    combinefunc = max,
    parallel = safe,
    sortop = operator (>)
    );

【讨论】:

【参考方案5】:

这不是 sqoop 的问题。 Postgres 不允许 uuid 上的 min/max。每个 uuid 都是唯一的,不会被认为比其他的更大/更小。

要在 sqoop 中解决此问题,您可能需要使用其他字段作为拆分键。我使用 created_At 时间戳作为我的拆分键。

【讨论】:

以上是关于postgresql 中不存在函数 min(uuid)的主要内容,如果未能解决你的问题,请参考以下文章

错误:PostgreSQL 中不存在列

表中不存在键,但它是 | Postgresql, timescaledb

如何仅从 PostgreSQL 的内部联接中不存在 id 的表中选择行?

PostgreSQL 错误:CASE 中不允许设置返回函数

如果不存在则插入行 postgresql

Postgresql插入如果不存在