web-dev-qa-db-fra.com

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

Les bases de données Postgres 8.4 et supérieures contiennent des tables communes dans le schéma public et des tables propres à la société dans le schéma company.
company les noms de schéma commencent toujours par 'company' et se terminent par le numéro d'entreprise.
Il peut donc y avoir des schémas tels que:

public
company1
company2
company3
...
companynn

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

search_path='company3,public'

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

par exemple:

select isSpecific('company3','tablenotincompany3schema')

devrait retourner false, et

select isSpecific('company3','tableincompany3schema')

devrait retourner true.

Dans tous les cas, la fonction doit vérifier uniquement le schéma companyn passé, pas les autres schémas.

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

127
Andrus

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 ces tables et ces vues sont montrées auxquelles l'utilisateur actuel a accès (en tant que propriétaire ou disposant de certains privilèges).

La requête démontrée par @kong peut renvoyer FALSE, mais la table peut toujours exister. Cela 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 1
   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 entre les versions principales et entre différents SGBDR. Mais la mise en œuvre est lente, car Postgres doit utiliser des vues sophistiquées pour se conformer à la norme (_information_schema.tables_ est un exemple assez simple). Et certaines informations (comme les OID) se perdent dans la traduction des catalogues système - qui transportent toutes les informations.

Catalogues système

Votre question était:

Comment vérifier si une table existe?

_SELECT EXISTS (
   SELECT 1 
   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_class_ et _pg_namespace_ directement, ce qui est également considérablement plus rapide. Cependant, selon la documentation sur _pg_class_ :

Le catalogue _pg_class_ catalogue les tables et la plupart des éléments contenant des colonnes ou similaires à une table. Ceci inclut index (mais voir aussi _pg_index_), séquences, vues, vues matérialisées, - types composites, et tables TOAST;

Pour cette question particulière, vous pouvez également utiliser le vue système pg_tables . Un peu plus simple et plus portable entre les principales versions de Postgres (ce qui n’inquiète guère pour cette requête de base):

_SELECT EXISTS (
   SELECT 1 
   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 voulez demander:

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

_SELECT EXISTS (
   SELECT 1 
   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 to regclass

_SELECT 'schema_name.table_name'::regclass
_

Ceci déclenche une exception si la table (éventuellement qualifiée de schéma) (ou un autre objet occupant ce nom) n'existe pas.

Si vous ne qualifiez pas le nom de la table de manière schématique, la conversion en regclass par défaut est search_path et renvoie le OID pour le premier 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_catalog_ et _pg_temp_ (le schéma des objets temporaires de la session en cours) font automatiquement partie du _search_path_.

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

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

to_regclass(rel_name) in Postgres 9.4+

Beaucoup plus simple maintenant:

_SELECT to_regclass('schema_name.table_name');
_

Identique à la distribution, but ​​il retourne ...

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

235

Peut-être utiliser information_schema :

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

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

Trois versions de mon ancienne bibliothèque SwissKnife: relname_exists(anyThing), relname_normalized(anyThing) et relnamechecked_to_array(anyThing). Toutes les vérifications de la table pg_catalog.pg_class, et renvoie les types de données universels standard (boolean, text ou text []) .

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