如何检查给定架构中是否存在表


148

Postgres 8.4和更高版本的数据库包含public架构中的通用表和架构中的公司特定表company
company模式名称始终'company'以公司编号开头和结尾。
因此,可能会有类似以下的模式:

public
company1
company2
company3
...
companynn

应用程序始终与单个公司一起使用。
search_path相应指定在ODBC或连接Npgsql的字符串,如:

search_path='company3,public'

您如何检查指定表中是否存在给定表companyn

例如:

select isSpecific('company3','tablenotincompany3schema')

应该返回false,并且

select isSpecific('company3','tableincompany3schema')

应该回来true

无论如何,该函数应仅检查companyn传递的架构,而不检查其他架构。

如果给定的表public和传递的模式中都存在一个表,则该函数应返回true
它应该适用于Postgres 8.4或更高版本。

Answers:


283

这取决于您要精确测试的内容。

信息模式?

要查找“表是否存在”(无论是谁问的),严格来说,查询信息模式(information_schema.tables)是不正确的,因为(根据文档):

仅显示当前用户有权访问的那些表和视图(通过成为所有者或具有某些特权)。

@kong提供的查询可以返回FALSE,但是表仍然存在。它回答了这个问题:

如何检查表(或视图)是否存在,当前用户是否有权访问它?

SELECT EXISTS (
   SELECT FROM information_schema.tables 
   WHERE  table_schema = 'schema_name'
   AND    table_name   = 'table_name'
   );

信息模式主要用于在主要版本和不同RDBMS之间保持可移植性。但是实现速度很慢,因为Postgres必须使用复杂的视图来遵守该标准(这information_schema.tables是一个非常简单的示例)。而且某些信息(例如OID)会从系统目录的翻译中丢失- 实际上,携带着所有信息。

系统目录

您的问题是:

如何检查表是否存在?

SELECT EXISTS (
   SELECT FROM pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   AND    c.relkind = 'r'    -- only tables
   );

使用系统目录pg_classpg_namespace直接,这也是相当快。但是,根据以下文档pg_class

目录pg_class目录表和其他大多数所有有字段或者是那些类似表的东西。这包括索引(但也请参见pg_index),序列视图实例化视图复合类型TOAST表

对于此特定问题,您还可以使用系统视图pg_tables。在主要的Postgres版本中更简单,更易于移植(此基本查询几乎无需关注):

SELECT EXISTS (
   SELECT FROM pg_tables
   WHERE  schemaname = 'schema_name'
   AND    tablename  = 'table_name'
   );

标识符在上述所有对象中必须唯一。如果您想问:

如何检查给定架构中的表或类似对象的名称?

SELECT EXISTS (
   SELECT FROM pg_catalog.pg_class c
   JOIN   pg_catalog.pg_namespace n ON n.oid = c.relnamespace
   WHERE  n.nspname = 'schema_name'
   AND    c.relname = 'table_name'
   );

替代方法:强制转换为 regclass

SELECT 'schema_name.table_name'::regclass

引发一个例外,如果(任选模式修饰)表(或其它物体占据该名称)不存在。

如果您不对表名进行模式限定,则强制转换regclass为,search_path并返回找到的第一个表的OID-如果该表不在所列模式中,则返回异常。请注意,系统架构pg_catalogpg_temp(当前会话的临时对象的架构)自动成为的一部分search_path

您可以使用它并在函数中捕获可能的异常。例:

上面的查询避免了可能的异常,因此速度稍快。

to_regclass(rel_name) 在Postgres 9.4+

现在简单得多:

SELECT to_regclass('schema_name.table_name');

与演员表相同,返回...

... null而不是如果找不到名称则抛出错误


4
从外壳:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo 2015年

1
有什么原因不使用pg_tables吗?
m0meni

1
pg_tables“如何检查表是否存在”实际上是一个好主意。(检查表只,不作其他用途,如上面所解释的同时,pg_tables是涉及多个表的视图(pg_classpg_namespacepg_tablespace),这是一个比较昂贵的最重要的原因是:我用查询pg_class直接,并没有想到pg_tables写这个答案时,我加入它上面现在,感谢。
欧文Brandstetter修改

1
@ sage88:是的,我删除了我不正确的评论。您可以使用它pg_my_temp_schema()来获取实际临时模式的OID(如果存在)。(但是视图中information_schema不包括OID。您可以SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema())测试有几个弱点。正确的测试可能table_schema LIKE 'pg\_temp\_%'或更严格:table_schema ~ '^pg_temp_\d+$'
Erwin Brandstetter

1
@PeterKrauss如果尝试在9.4之前的postgres版本上使用to_regclass函数,则会收到该错误。必须有9.4+
spetz83 '17

44

也许使用information_schema

SELECT EXISTS(
    SELECT * 
    FROM information_schema.tables 
    WHERE 
      table_schema = 'company3' AND 
      table_name = 'tableincompany3schema'
);

0

对于PostgreSQL 9.3或更低版本...或喜欢将其标准化为文本的人

我的老SwissKnife库的三种口味:relname_exists(anyThing)relname_normalized(anyThing)relnamechecked_to_array(anyThing)。所有检查均来自pg_catalog.pg_class表,并返回标准的通用数据类型(布尔文本文本 [])。

/**
 * From my old SwissKnife Lib to your SwissKnife. License CC0.
 * Check and normalize to array the free-parameter relation-name.
 * Options: (name); (name,schema), ("schema.name"). Ignores schema2 in ("schema.name",schema2).
 */
CREATE FUNCTION relname_to_array(text,text default NULL) RETURNS text[] AS $f$
     SELECT array[n.nspname::text, c.relname::text]
     FROM   pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace,
            regexp_split_to_array($1,'\.') t(x) -- not work with quoted names
     WHERE  CASE
              WHEN COALESCE(x[2],'')>'' THEN n.nspname = x[1]      AND c.relname = x[2]
              WHEN $2 IS NULL THEN           n.nspname = 'public'  AND c.relname = $1
              ELSE                           n.nspname = $2        AND c.relname = $1
            END
$f$ language SQL IMMUTABLE;

CREATE FUNCTION relname_exists(text,text default NULL) RETURNS boolean AS $wrap$
  SELECT EXISTS (SELECT relname_to_array($1,$2))
$wrap$ language SQL IMMUTABLE;

CREATE FUNCTION relname_normalized(text,text default NULL,boolean DEFAULT true) RETURNS text AS $wrap$
  SELECT COALESCE(array_to_string(relname_to_array($1,$2), '.'), CASE WHEN $3 THEN '' ELSE NULL END)
$wrap$ language SQL IMMUTABLE;
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.