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


75

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

CREATE TYPE abc ...

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

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


您知道如果表仍在使用某个类型,则无法删除或替换该类型?
AH

4
如果要避免在事务中由于先前尝试失败的事务而已经创建的类型而导致错误,则始终可以在创建语句之前DROP TYPE IF EXISTS。
坎帕

Answers:


101

我在此处添加了用于在简单脚本中创建类型的完整解决方案,而无需为此创建函数。

--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$$;

4
当使用模式时,这不是很正确,因为pg_type表中存在一个类型并不一定意味着它存在于当前搜索路径的任何模式中。您也需要在typnamespace上选择的ISTM,但是我不确定如何。
罗格

@Cromax请参阅我的答案的更简单版本
罗格

@rog似乎是合理的。8)而且它不依赖PSQL的功能(如regtype),因此它似乎实际上是更好的解决方案。8)
Cromax

为什么这么多,这是唯一,最好的答案?如果我用它回答并在具有该类型的一个版本的数据库上运行创建类型,则使用此sql我的旧类型永远不会更改。从这个问题可以清楚地看出,数据库中可能存在或可能不存在我们需要的类型。
n3ko

这个对我不起作用,所以我选择了EXCEPTION。
iElectric

60

到目前为止,我发现的最简单的解决方案是受@Cromax的答案启发,可以应对模式:

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

正是您可能真正期望的-我们将CREATE TYPE语句包装在异常处理程序中,这样它才不会中止当前事务。


这是唯一对我有用的例子。谢谢!
iElectric

我是这种语言的新手,我想做类似的事情,但不要重载现有的类型。所以我想检查一个类型是否存在,如果不存在,请创建它。我不确定EXCEPTION这里是什么意思。类型是否已经过载,或者我可以按原样使用?
卡萨(Kajsa),

25

您可以在pg_type表格中查看:

select exists (select 1 from pg_type where typname = 'abc');

如果是这样,则abc存在。


6
对于从Google寻求此解决方案的人们来说,这种方法有一个小小的警告。如果您在模式中创建了一个类型,例如“ my_schema”,那么即使您正在签入另一个模式,该条件仍将返回true。完整的查询为select exists (select 1 from pg_type where typname = 'abc' and typnamespace = (select oid from pg_namespace where nspname = 'my_schema'))。如果使用单模式配置,请用“ public”替换“ my_schema”。
罗汉·帕布

19

实际上,Postgres没有CREATE OR REPLACE类型的功能。因此最好的方法是删除它:

DROP TYPE IF EXISTS YOUR_TYPE;
CREATE TYPE YOUR_TYPE AS (
    id      integer,
    field   varchar
);

简单的解决方案始终是最好的解决方案。


14
如果表当前使用该类型怎么办?
Shane Hughes

2
@Shane然后DROP将引发错误。但是DROP ... CASCADE,如果在特定情况下丢失数据是可以接受的,则可以使用它也删除依赖的对象。
Cromax '18

8

为了解决@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 $$;

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


很好的解释,谢谢!到目前为止,这似乎是最好的答案。可惜它没有得到更多的关注。
通配符

1
谢谢你不客气。另请参阅@rog的改写答案,它更短。(从我的POV来看)它的一个缺点是,如果检查不止一次发生,则执行速度会较慢,这是因为每次类型存在时都会引发并捕获异常(并且在第一次执行后便会存在)。但这毕竟不是那么重要。8)
Cromax,

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

1

我正在尝试做同样的事情,确保类型存在。

我使用--echo-hidden-E)选项启动了psql并输入\dT

$ psql -E
psql (9.1.9)
testdb=> \dT
********* QUERY **********
SELECT n.nspname as "Schema",
  pg_catalog.format_type(t.oid, NULL) AS "Name",
  pg_catalog.obj_description(t.oid, 'pg_type') as "Description"
FROM pg_catalog.pg_type t
     LEFT JOIN pg_catalog.pg_namespace n ON n.oid = t.typnamespace
WHERE (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))
  AND NOT EXISTS(SELECT 1 FROM pg_catalog.pg_type el WHERE el.oid = t.typelem AND el.typarray = t.oid)
      AND n.nspname <> 'pg_catalog'
      AND n.nspname <> 'information_schema'
  AND pg_catalog.pg_type_is_visible(t.oid)
ORDER BY 1, 2;
**************************
 List of data types
 Schema |       Name       | Description 
--------+------------------+-------------
 public | errmsg_agg_state | 
(1 row)

如果您使用的是schemas和search_path(我是),则可能需要保留pg_catalog.pg_type_is_visible(t.oid)检查。我不知道WHERE中的所有条件都在做什么,但这些条件似乎与我的情况无关。目前正在使用:

SELECT 1 FROM pg_catalog.pg_type as t
   WHERE typname = 'mytype' AND pg_catalog.pg_type_is_visible(t.oid);

1

更通用的解决方案

CREATE OR REPLACE FUNCTION create_type(name text, _type text) RETURNS 
integer AS $$
DECLARE v_exists INTEGER;

BEGIN
    SELECT into v_exists (SELECT 1 FROM pg_type WHERE typname = name);
    IF v_exists IS NULL THEN
            EXECUTE format('CREATE TYPE %I AS %s', name, _type);
    END IF;
    RETURN v_exists;
END;
$$ LANGUAGE plpgsql;

然后可以这样称呼它:

select create_type('lwm2m_instancetype', 'enum (''single'',''multiple'')');


0

另一种选择

WITH namespace AS(
    SELECT oid 
        FROM pg_namespace 
        WHERE nspname = 'my_schema'
),
type_name AS (
    SELECT 1 type_exist  
        FROM pg_type 
        WHERE typname = 'my_type' AND typnamespace = (SELECT * FROM namespace)
)
SELECT EXISTS (SELECT * FROM type_name);

0

这可以很好地与模式配合使用,并避免了异常处理:

DO $$
BEGIN
    IF NOT EXISTS (
      SELECT 1 FROM pg_type t
      LEFT JOIN pg_namespace p ON t.typnamespace=p.oid
      WHERE t.typname='my_type' AND p.nspname='my_schema'
    ) THEN
        CREATE TYPE my_schema.my_type AS (/* fields go here */);
    END IF;
END
$$;
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.