检查PostgreSQL中是否已存在用户定义的类型

Lar*_*rry 53 postgresql user-defined-types

假设我在DB中创建了一些用户定义的类型,

CREATE TYPE abc ...

那么可以确定用户定义的类型是否存在?也许,使用任何postgres信息表?

主要原因是因为PostgreSQL似乎不支持CREATE OR REPLACE TYPE ...,并且如果某个类型被多次创建,我希望能够先删除现有的类型,然后重新加载新的类型.

blu*_*ish 87

我在这里添加了在简单脚本中创建类型的完整解决方案,而无需为此目的创建函数.

--create types
DO $$
BEGIN
    IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'my_type') THEN
        CREATE TYPE my_type AS
        (
            --my fields here...
        );
    END IF;
    --more types here...
END$$;
Run Code Online (Sandbox Code Playgroud)

  • 这在使用模式时不太正确,因为 pg_type 表中存在类型这一事实并不一定意味着它存在于当前搜索路径中的任何模式中。您也需要在 typnamespace 上选择 ISTM,但我不确定如何选择。 (4认同)

rog*_*rog 35

迄今为止我发现的最简单的解决方案是在@ Cromax的答案的启发下应对模式,这是:

DO $$ BEGIN
    CREATE TYPE my_type AS (/* fields go here */);
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;
Run Code Online (Sandbox Code Playgroud)

正是您可能期望的 - 我们只是将CREATE TYPE语句包装在异常处理程序中,因此它不会中止当前事务.


mu *_*ort 18

您可以查看pg_type表格:

select exists (select 1 from pg_type where typname = 'abc');
Run Code Online (Sandbox Code Playgroud)

如果这是真的那么abc存在.

  • ***注意***:这是*典型*而不是典型**E**名字! (4认同)
  • 对于从Google寻求此解决方案的人来说,此方法有一个小警告。如果您在模式中创建了一个类型,例如“ my_schema”,那么即使您正在签入另一个模式,该条件仍将返回true。完整的查询将是“选择存在”(从pg_type中选择1,其中typname ='abc'和typnamespace =(从pg_namespace中选择oid,其中nspname ='my_schema'))。如果您使用单模式配置,请用“ public”替换“ my_schema”。 (2认同)

Nul*_*lik 9

实际上,Postgres没有CREATE OR REPLACE类型的功能.所以最好的办法是放弃它:

DROP TYPE IF EXISTS YOUR_TYPE;
CREATE TYPE YOUR_TYPE AS (
    id      integer,
    field   varchar
);
Run Code Online (Sandbox Code Playgroud)

简单的解决方案总是最好的.

  • 我建议许多人(包括我自己)自然而然地厌恶那些甚至会丢失一点数据的机制。因此,尽管简单的解决方案通常是最好的,但这可能是一个反例。 (14认同)
  • 如果表当前使用该类型该怎么办? (8认同)
  • @Shane然后`DROP`会抛出一个错误.您可以使用`DROP ... CASCADE`来删除依赖对象 - 如果在该特定情况下丢失数据是可接受的. (2认同)

Elo*_*Jr. 6

-- All of this to create a type if it does not exist
CREATE OR REPLACE FUNCTION create_abc_type() RETURNS integer AS $$
DECLARE v_exists INTEGER;

BEGIN
    SELECT into v_exists (SELECT 1 FROM pg_type WHERE typname = 'abc');
    IF v_exists IS NULL THEN
        CREATE TYPE abc AS ENUM ('height', 'weight', 'distance');
    END IF;
    RETURN v_exists;
END;
$$ LANGUAGE plpgsql;

-- Call the function you just created
SELECT create_abc_type();

-- Remove the function you just created
DROP function create_abc_type();
-----------------------------------
Run Code Online (Sandbox Code Playgroud)


Han*_*nde 6

受@Cromax答案的启发,这里有一个使用系统目录信息函数的替代解决方案to_regtype,它避免了异常子句的额外开销,但仍然检查类型是否存在的正确模式:

DO $$ BEGIN
    IF to_regtype('my_schema.abc') IS NULL THEN
        CREATE TYPE my_schema.abc ... ;
    END IF;
END $$;
Run Code Online (Sandbox Code Playgroud)

在使用默认模式的情况下public,它看起来像:

DO $$ BEGIN
    IF to_regtype('abc') IS NULL THEN
        CREATE TYPE abc ... ;
    END IF;
END $$;
Run Code Online (Sandbox Code Playgroud)


Cro*_*max 5

为了解决@rog对@bluish答案的困境,使用regtype数据类型可能更合适。考虑一下:

DO $$ BEGIN
    PERFORM 'my_schema.my_type'::regtype;
EXCEPTION
    WHEN undefined_object THEN
        CREATE TYPE my_schema.my_type AS (/* fields go here */);
END $$;
Run Code Online (Sandbox Code Playgroud)

PERFORM子句类似于SELECT,但它会丢弃结果,因此基本上我们正在检查是否有可能将其强制转换为'my_schema.my_type'(或只是'my_type'不关心特定于模式的)实际注册类型。如果确实存在该类型,则不会发生任何“错误”,并且由于RETURN整个块而将结束-由于该类型my_type已经存在,因此不会进行任何更改。但是,如果无法进行强制转换,则会抛出42704带有标记为的错误代码undefined_object。因此,在接下来的几行中,我们尝试捕获该错误,如果发生这种情况,我们只需创建新的数据类型。

  • 谢谢你不客气。另请参阅@rog 的修改答案,它更短。它的一个缺点(从我的观点来看)是执行速度较慢,如果检查发生多次,因为每次类型存在时都会抛出并捕获异常(并且在第一次执行后它将存在)。但也许终究没那么重要。8) (2认同)