Cómo verificar si existe una tabla en un esquema dado

149

Las bases de datos Postgres 8.4 y superiores contienen tablas comunes en el publicesquema y tablas específicas de la compañía en el companyesquema.
companylos nombres de esquema siempre comienzan 'company'y terminan con el número de la compañía.
Entonces puede haber esquemas como:

public
company1
company2
company3
...
companynn

Una aplicación siempre funciona con una sola empresa.
El search_pathse especifica en consecuencia en la cadena de conexión odbc o npgsql, como:

search_path='company3,public'

¿Cómo comprobaría si existe una tabla dada en un companynesquema específico ?

p.ej:

select isSpecific('company3','tablenotincompany3schema')

debería volver falsey

select isSpecific('company3','tableincompany3schema')

Debería volver true.

En cualquier caso, la función debe verificar solo el companynesquema pasado, no otros esquemas.

Si existe una tabla dada en ambos publicy en el esquema pasado, la función debería regresar true.
Debería funcionar para Postgres 8.4 o posterior.

Andrus
fuente

Respuestas:

284

Depende de lo que quieras probar exactamente .

Esquema de información?

Para encontrar "si la tabla existe" ( no importa quién pregunte ), consultar el esquema de información ( information_schema.tables) es incorrecto , estrictamente hablando, porque ( según la documentación ):

Solo se muestran aquellas tablas y vistas a las que el usuario actual tiene acceso (por ser el propietario o tener algún privilegio).

La consulta proporcionada por @kong puede regresar FALSE, pero la tabla aún puede existir. Responde a la pregunta:

¿Cómo verificar si existe una tabla (o vista) y si el usuario actual tiene acceso a ella?

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

El esquema de información es principalmente útil para mantenerse portátil en las principales versiones y en diferentes RDBMS. Pero la implementación es lenta, porque Postgres tiene que usar vistas sofisticadas para cumplir con el estándar ( information_schema.tableses un ejemplo bastante simple). Y parte de la información (como los OID) se pierde en la traducción de los catálogos del sistema, que en realidad contienen toda la información.

Catálogos del sistema

Tu pregunta fue:

¿Cómo verificar si existe una tabla?

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

Utilice los catálogos del sistema pg_classy pg_namespacedirectamente, que también es considerablemente más rápido. Sin embargo, según la documentación sobrepg_class :

El catálogo pg_classcataloga tablas y casi todo lo demás que tiene columnas o es similar a una tabla. Esto incluye índices (pero vea también pg_index), secuencias , vistas , vistas materializadas , tipos compuestos y tablas TOAST ;

Para esta pregunta en particular, también puede usar la vista del sistemapg_tables . Un poco más simple y portátil en las principales versiones de Postgres (lo cual no es motivo de preocupación para esta consulta básica):

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

Los identificadores deben ser únicos entre todos los objetos mencionados anteriormente. Si quieres preguntar:

¿Cómo verificar si se toma el nombre de una tabla u objeto similar en un esquema dado?

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

Alternativa: emitir a regclass

SELECT 'schema_name.table_name'::regclass

Esto genera una excepción si la tabla (opcionalmente calificada por el esquema) (u otro objeto que ocupe ese nombre) no existe.

Si no califica el esquema del nombre de la tabla, una conversión a los regclassvalores predeterminados search_pathy devuelve el OID para la primera tabla encontrada, o una excepción si la tabla no se encuentra en ninguno de los esquemas enumerados. Tenga en cuenta que los esquemas del sistema pg_catalogy pg_temp(el esquema para objetos temporales de la sesión actual) son automáticamente parte de search_path.

Puede usar eso y detectar una posible excepción en una función. Ejemplo:

Una consulta como la anterior evita posibles excepciones y, por lo tanto, es un poco más rápida.

to_regclass(rel_name) en Postgres 9.4+

Mucho más simple ahora:

SELECT to_regclass('schema_name.table_name');

Igual que el elenco, pero vuelve ...

... nulo en lugar de arrojar un error si no se encuentra el nombre

Erwin Brandstetter
fuente
44
desde shell:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo
1
¿Hay alguna razón por la que no estás usando pg_tables ?
m0meni
1
pg_tableses realmente una buena idea para "¿Cómo verificar si existe una tabla?" (Comprobación de tablas sólo ., No para otros fines, como se explica más arriba también, pg_tableses una vista que implica varias mesas ( pg_class, pg_namespace, pg_tablespace), que es un poco más caro La razón más importante:. Estoy acostumbrado a consulta pg_classdirecta y no lo hice piense pg_tablesal escribir esta respuesta. La agregué arriba ahora, gracias.
Erwin Brandstetter
1
@ sage88: Correcto, eliminé mi comentario incorrecto. Puede usar pg_my_temp_schema()para obtener el OID del esquema temporal real si existe. (Pero las vistas en el information_schemano incluyen OID. Podría SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Su prueba tiene varias debilidades. Una prueba correcta sería table_schema LIKE 'pg\_temp\_%'o más estrictas: table_schema ~ '^pg_temp_\d+$'.
Erwin Brandstetter
1
@PeterKrauss Obtendrá ese error si intenta utilizar la función to_regclass en una versión de postgres anterior a 9.4. Debe tener 9.4+
spetz83
44

Quizás use información_esquema :

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

Para PostgreSQL 9.3 o menos ... O a quién le gusta todo normalizado a texto

Tres sabores de mi biblioteca SwissKnife antigua: relname_exists(anyThing), relname_normalized(anyThing)y relnamechecked_to_array(anyThing). Todas las comprobaciones de la tabla pg_catalog.pg_class y devuelve tipos de datos universales estándar ( booleano , texto o texto []).

/**
 * 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;
Peter Krauss
fuente