Como verificar se existe uma tabela em um determinado esquema

149

Os bancos de dados do Postgres 8.4 e superiores contêm tabelas comuns no publicesquema e tabelas específicas da empresa no companyesquema.
companynomes de esquema sempre começam 'company'e terminam com o número da empresa.
Portanto, pode haver esquemas como:

public
company1
company2
company3
...
companynn

Um aplicativo sempre funciona com uma única empresa.
O search_pathé especificado de acordo na cadeia de conexão odbc ou npgsql, como:

search_path='company3,public'

Como você verificaria se uma determinada tabela existe em um companynesquema especificado ?

por exemplo:

select isSpecific('company3','tablenotincompany3schema')

deve retornar falsee

select isSpecific('company3','tableincompany3schema')

deve retornar true.

De qualquer forma, a função deve verificar apenas o companynesquema passado, não outros esquemas.

Se existir uma tabela específica no publicesquema passado e passado, a função deve retornar true.
Ele deve funcionar no Postgres 8.4 ou posterior.

Andrus
fonte

Respostas:

283

Depende do que você deseja testar exatamente .

Esquema de informação?

Para descobrir "se a tabela existe" ( não importa quem está perguntando ), a consulta do esquema de informações ( information_schema.tables) está incorreta , estritamente falando, porque ( por documentação ):

Somente essas tabelas e visualizações são mostradas às quais o usuário atual tem acesso (por ser o proprietário ou ter algum privilégio).

A consulta fornecida pelo @kong pode retornar FALSE, mas a tabela ainda pode existir. Responde à pergunta:

Como verificar se existe uma tabela (ou exibição) e se o usuário atual tem acesso a ela?

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

O esquema de informações é principalmente útil para se manter portátil nas principais versões e nos diferentes RDBMS. Mas a implementação é lenta, porque o Postgres precisa usar visualizações sofisticadas para estar em conformidade com o padrão ( information_schema.tablesé um exemplo bastante simples). E algumas informações (como OIDs) são perdidas na tradução dos catálogos do sistema - que na verdade carregam todas as informações.

Catálogos do sistema

Sua pergunta foi:

Como verificar se existe uma tabela?

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

Use os catálogos do sistema pg_classe pg_namespacediretamente, o que também é consideravelmente mais rápido. No entanto, por documentação empg_class :

O catálogo pg_classcataloga tabelas e quase todo o resto que possui colunas ou é semelhante a uma tabela. Isso inclui índices (mas veja também pg_index), sequências , visualizações , visualizações materializadas , tipos compostos e tabelas TOAST ;

Para esta pergunta em particular, você também pode usar a visualizaçãopg_tables do sistema . Um pouco mais simples e mais portátil nas principais versões do Postgres (o que dificilmente preocupa essa consulta básica):

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

Os identificadores devem ser exclusivos entre todos os objetos mencionados acima. Se você quiser perguntar:

Como verificar se um nome para uma tabela ou objeto semelhante em um determinado esquema é usado?

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: transmitir para regclass

SELECT 'schema_name.table_name'::regclass

Isso gera uma exceção se a tabela (opcionalmente qualificada pelo esquema) (ou outro objeto que ocupa esse nome) não existir.

Se você não qualificar o nome da tabela, uma conversão para o regclasspadrãosearch_path e retorna o OID para a primeira tabela encontrada - ou uma exceção se a tabela não estiver em nenhum dos esquemas listados. Observe que os esquemas do sistema pg_cataloge pg_temp(o esquema para objetos temporários da sessão atual) fazem parte automaticamente do search_path.

Você pode usar isso e capturar uma possível exceção em uma função. Exemplo:

Uma consulta como a anterior evita possíveis exceções e, portanto, é um pouco mais rápida.

to_regclass(rel_name) no Postgres 9.4 ou superior

Muito mais simples agora:

SELECT to_regclass('schema_name.table_name');

Igual ao elenco, mas retorna ...

... nulo em vez de gerar um erro se o nome não for encontrado

Erwin Brandstetter
fonte
4
do shell:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo
1
Existe algum motivo para você não estar usando o pg_tables ?
M0meni
1
pg_tablesé realmente uma boa ideia para "Como verificar se existe uma tabela?" (Verificação de mesas única ., Não para outros fins, como explicado acima Além disso, pg_tablesé uma visão envolvendo várias mesas ( pg_class, pg_namespace, pg_tablespace), que é um pouco mais caro A razão mais importante:. Estou habituado a consulta pg_classdiretamente e não fez pensar pg_tablesao escrever esta resposta eu adicionei-o acima agora, obrigado..
Erwin Brandstetter
1
@ sage88: Certo, removi meu comentário incorreto. Você pode usar pg_my_temp_schema()para obter o OID do esquema temporário real, se existir. (Mas as visualizações no information_schemanão incluem OIDs. Você poderia SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Seu teste tem várias fraquezas. Um teste correto seria table_schema LIKE 'pg\_temp\_%'ou mais rigorosas: table_schema ~ '^pg_temp_\d+$'.
Erwin Brandstetter
1
@PeterKrauss Você receberá esse erro se tentar usar a função to_regclass em uma versão do postgres anterior à 9.4. Deve ter 9.4+
spetz83
44

Talvez use information_schema :

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

Para PostgreSQL 9.3 ou menos ... Ou quem gosta de tudo normalizado para texto

Três sabores de minha biblioteca SwissKnife idade: relname_exists(anyThing), relname_normalized(anyThing)e relnamechecked_to_array(anyThing). Todas as verificações da tabela pg_catalog.pg_class e retornam tipos de dados universais padrão ( booleano , texto ou 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
fonte