Comment vérifier si une table existe dans un schéma donné

149

Les bases de données Postgres 8.4 et supérieures contiennent des tables communes dans le publicschéma et des tables spécifiques à l'entreprise dans le companyschéma.
companyles noms de schéma commencent 'company'et se terminent toujours par le numéro d'entreprise.
Il peut donc y avoir des schémas comme:

public
company1
company2
company3
...
companynn

Une application fonctionne toujours avec une seule entreprise.
Le search_pathest spécifié en conséquence dans la chaîne de connexion odbc ou npgsql, comme:

search_path='company3,public'

Comment vérifieriez-vous si une table donnée existe dans un companynschéma spécifié ?

par exemple:

select isSpecific('company3','tablenotincompany3schema')

devrait revenir false, et

select isSpecific('company3','tableincompany3schema')

devrait revenir true.

Dans tous les cas, la fonction ne doit vérifier que le companynschéma passé, pas les autres schémas.

Si une table donnée existe à la fois dans publicle schéma et dans le schéma passé, la fonction doit retourner true.
Cela devrait fonctionner pour Postgres 8.4 ou version ultérieure.

Andrus
la source

Réponses:

284

Cela dépend de ce que vous voulez tester exactement .

Schéma d'information?

Pour trouver "si la table existe" ( peu importe qui le demande ), interroger le schéma d'information ( information_schema.tables) est incorrect , à proprement parler, car ( par documentation ):

Seules les tables et vues auxquelles l'utilisateur actuel a accès sont affichées (en étant le propriétaire ou en ayant certains privilèges).

La requête fournie par @kong peut renvoyer FALSE, mais la table peut toujours exister. Il répond à la question:

Comment vérifier si une table (ou une vue) existe et que l'utilisateur actuel y a accès?

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

Le schéma d'information est principalement utile pour rester portable dans les versions principales et dans différents SGBDR. Mais l'implémentation est lente, car Postgres doit utiliser des vues sophistiquées pour se conformer à la norme ( information_schema.tablesc'est un exemple assez simple). Et certaines informations (comme les OID) sont perdues lors de la traduction à partir des catalogues système - qui contiennent en fait toutes les informations.

Catalogues système

Votre question était:

Comment vérifier si une table existe?

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

Utilisez les catalogues système pg_classet pg_namespacedirectement, ce qui est également beaucoup plus rapide. Cependant, par documentation surpg_class :

Le catalogue pg_classrépertorie les tables et presque tout le reste qui a des colonnes ou qui est autrement similaire à une table. Cela inclut les index (mais voir aussi pg_index), les séquences , les vues , les vues matérialisées , les types composites et les tables TOAST ;

Pour cette question particulière, vous pouvez également utiliser la vue systèmepg_tables . Un peu plus simple et plus portable dans les principales versions de Postgres (ce qui n'est guère préoccupant pour cette requête de base):

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

Les identifiants doivent être uniques parmi tous les objets mentionnés ci-dessus. Si vous souhaitez demander:

Comment vérifier si un nom pour une table ou un objet similaire dans un schéma donné est pris?

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

Alternative: cast vers regclass

SELECT 'schema_name.table_name'::regclass

Cela déclenche une exception si la table (éventuellement qualifiée par le schéma) (ou tout autre objet occupant ce nom) n'existe pas.

Si vous ne qualifiez pas de schéma le nom de la table, une conversion par regclassdéfaut en search_pathet renvoie l'OID de la première table trouvée - ou une exception si la table ne figure dans aucun des schémas répertoriés. Notez que les schémas système pg_cataloget pg_temp(le schéma pour les objets temporaires de la session en cours) font automatiquement partie du search_path.

Vous pouvez l'utiliser et intercepter une exception possible dans une fonction. Exemple:

Une requête comme ci-dessus évite d'éventuelles exceptions et est donc légèrement plus rapide.

to_regclass(rel_name) dans Postgres 9.4+

Beaucoup plus simple maintenant:

SELECT to_regclass('schema_name.table_name');

Identique au casting, mais ça revient ...

... null plutôt que de lancer une erreur si le nom n'est pas trouvé

Erwin Brandstetter
la source
4
de shell:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
brauliobo
1
Y a-t-il une raison pour laquelle vous n'utilisez pas pg_tables ?
m0meni le
1
pg_tablesest en fait une bonne idée pour "Comment vérifier si une table existe?" (Vérification des tables seulement ., Et non à d' autres fins, comme expliqué ci - dessus également, pg_tablesest une vue impliquant plusieurs tables ( pg_class, pg_namespace, pg_tablespace), ce qui est un peu plus cher La raison la plus importante. Je suis habitué à la requête pg_classdirectement et n'a pas pensez pg_tablesà en écrivant cette réponse. Je l'ai ajouté ci-dessus maintenant, merci.
Erwin Brandstetter
1
@ sage88: C'est vrai, j'ai supprimé mon commentaire incorrect. Vous pouvez utiliser pg_my_temp_schema()pour obtenir l'OID du schéma temporaire réel s'il existe. (Mais les vues dans le information_scheman'incluent pas d'OID. Vous pourriez SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Votre test a plusieurs faiblesses. Un test correct serait table_schema LIKE 'pg\_temp\_%'ou plus stricte: table_schema ~ '^pg_temp_\d+$'.
Erwin Brandstetter
1
@PeterKrauss Vous obtiendrez cette erreur si vous essayez d'utiliser la fonction to_regclass sur une version de postgres antérieure à la 9.4. Doit avoir 9.4+
spetz83
44

Utilisez peut-être information_schema :

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

Pour PostgreSQL 9.3 ou moins ... Ou qui aime tout normalisé au texte

Trois saveurs de mon ancienne bibliothèque SwissKnife: relname_exists(anyThing), relname_normalized(anyThing)et relnamechecked_to_array(anyThing). Tous les contrôles de la table pg_catalog.pg_class , et retourne les types de données universels standard ( booléen , texte ou texte []).

/**
 * 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
la source