Come verificare se esiste una tabella in un determinato schema


149

I database Postgres 8.4 e successivi contengono tabelle comuni nello publicschema e tabelle specifiche dell'azienda nello companyschema.
companyi nomi degli schemi iniziano sempre con 'company'e terminano con il numero dell'azienda.
Quindi potrebbero esserci schemi come:

public
company1
company2
company3
...
companynn

Un'applicazione funziona sempre con una singola azienda.
Il search_pathè specificato di conseguenza in ODBC o stringa di connessione npgsql, come:

search_path='company3,public'

Come verifichi se una determinata tabella esiste in uno companynschema specificato ?

per esempio:

select isSpecific('company3','tablenotincompany3schema')

dovrebbe tornare false, e

select isSpecific('company3','tableincompany3schema')

dovrebbe tornare true.

In ogni caso, la funzione dovrebbe controllare solo lo companynschema passato, non altri schemi.

Se esiste una determinata tabella in entrambi publice nello schema passato, la funzione dovrebbe restituire true.
Dovrebbe funzionare con Postgres 8.4 o successivo.

Risposte:


284

Dipende da cosa vuoi testare esattamente .

Schema informativo?

Per scoprire "se la tabella esiste" ( indipendentemente da chi lo sta chiedendo ), eseguire una query sullo schema di informazioni ( information_schema.tables) non è corretto , in senso stretto, perché ( per documentazione ):

Vengono mostrate solo le tabelle e le viste a cui l'utente corrente ha accesso (per mezzo del proprietario o dei privilegi).

La query fornita da @kong può tornare FALSE, ma la tabella può ancora esistere. Risponde alla domanda:

Come verificare se esiste una tabella (o vista) e l'utente corrente ha accesso ad essa?

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

Lo schema delle informazioni è utile soprattutto per essere trasportabile tra le versioni principali e tra diversi RDBMS. Ma l'implementazione è lenta, perché Postgres deve usare viste sofisticate per conformarsi allo standard ( information_schema.tablesè un esempio piuttosto semplice). E alcune informazioni (come gli OID) si perdono nella traduzione dai cataloghi di sistema, che in realtà contengono tutte le informazioni.

Cataloghi di sistema

La tua domanda era:

Come verificare se esiste una tabella?

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

Utilizza i cataloghi di sistema pg_classe pg_namespacedirettamente, che è anche molto più veloce. Tuttavia, per documentazione supg_class :

Il catalogo pg_classcataloga le tabelle e quasi tutto il resto con colonne o è simile a una tabella. Questo include indici (ma vedi anche pg_index), sequenze , viste , viste materializzate , tipi compositi e tabelle TOAST ;

Per questa domanda particolare puoi anche usare la vista di sistemapg_tables . Un po 'più semplice e portatile nelle principali versioni di Postgres (il che non preoccupa molto per questa query di base):

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

Gli identificatori devono essere univoci tra tutti gli oggetti sopra menzionati. Se vuoi chiedere:

Come verificare se viene preso un nome per una tabella o un oggetto simile in un determinato schema?

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: cast a regclass

SELECT 'schema_name.table_name'::regclass

Ciò solleva un'eccezione se la tabella (facoltativamente qualificata da schema) (o altro oggetto che occupa quel nome) non esiste.

Se non si qualifica lo schema del nome della tabella, un cast viene regclassimpostato in modo predefinito sull'OID search_pathe restituisce l'OID per la prima tabella trovata, oppure un'eccezione se la tabella non si trova in nessuno degli schemi elencati. Si noti che gli schemi di sistema pg_cataloge pg_temp(lo schema per oggetti temporanei della sessione corrente) fanno automaticamente parte di search_path.

Puoi usarlo e cogliere una possibile eccezione in una funzione. Esempio:

Una query come sopra evita possibili eccezioni ed è quindi leggermente più veloce.

to_regclass(rel_name) in Postgres 9.4+

Molto più semplice ora:

SELECT to_regclass('schema_name.table_name');

Come il cast, ma restituisce ...

... null anziché generare un errore se il nome non viene trovato


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

1
C'è qualche motivo per cui non stai usando pg_tables ?
m0meni,

1
pg_tablesè in realtà una buona idea per "Come verificare se esiste una tabella?" (Controllo di tabelle solo ., Non per altri scopi, come spiegato sopra Inoltre, pg_tablesè una vista che coinvolge più tabelle ( pg_class, pg_namespace, pg_tablespace), che è un po 'più costoso Il più importante motivo:. Sono abituato a interrogare pg_classdirettamente e no pensa pg_tablesquando scrivo questa risposta. L'ho aggiunta sopra ora, grazie.
Erwin Brandstetter,

1
@ sage88: giusto, ho rimosso il mio commento errato. È possibile utilizzare pg_my_temp_schema()per ottenere l'OID dello schema temporaneo reale se esiste. (Ma le visualizzazioni nel information_schemanon includono gli OID. Potresti SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Il tuo test presenta diversi punti deboli. Un test corretta sarebbe table_schema LIKE 'pg\_temp\_%'o più severe: table_schema ~ '^pg_temp_\d+$'.
Erwin Brandstetter,

1
@PeterKrauss Questo errore verrà visualizzato se si tenta di utilizzare la funzione to_regclass su una versione di postgres precedente alla 9.4. Deve avere 9.4+
spetz83 il

44

Forse usa information_schema :

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

0

Per PostgreSQL 9.3 o meno ... O a chi piace tutto normalizzato al testo

Tre sapori della mia vecchia biblioteca SwissKnife: relname_exists(anyThing), relname_normalized(anyThing)e relnamechecked_to_array(anyThing). Tutti i controlli dalla tabella pg_catalog.pg_class e restituiscono tipi di dati universali standard ( booleano , testo o testo []).

/**
 * 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;
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.