So überprüfen Sie, ob in einem bestimmten Schema eine Tabelle vorhanden ist

148

Postgres 8.4 und höhere Datenbanken enthalten allgemeine Tabellen im publicSchema und unternehmensspezifische Tabellen im companySchema.
companySchemanamen beginnen immer mit der Firmennummer 'company'und enden mit dieser.
Es kann also Schemata geben wie:

public
company1
company2
company3
...
companynn

Eine Anwendung funktioniert immer mit einer einzelnen Firma.
Das search_pathwird entsprechend in der Verbindungszeichenfolge odbc oder npgsql angegeben, wie:

search_path='company3,public'

Wie würden Sie überprüfen, ob eine bestimmte Tabelle in einem bestimmten companynSchema vorhanden ist?

z.B:

select isSpecific('company3','tablenotincompany3schema')

sollte zurückkehren false, und

select isSpecific('company3','tableincompany3schema')

sollte zurückkehren true.

In jedem Fall sollte die Funktion nur das übergebene companynSchema überprüfen , nicht andere Schemas.

Wenn eine bestimmte Tabelle in beiden publicund im übergebenen Schema vorhanden ist, sollte die Funktion zurückgegeben werden true.
Es sollte für Postgres 8.4 oder höher funktionieren.

Andrus
quelle

Antworten:

282

Es hängt davon ab, was Sie genau testen möchten .

Informationsschema?

Um herauszufinden, ob die Tabelle existiert ( egal wer fragt ), ist das Abfragen des Informationsschemas ( information_schema.tables) streng genommen falsch , weil ( gemäß Dokumentation ):

Es werden nur die Tabellen und Ansichten angezeigt, auf die der aktuelle Benutzer Zugriff hat (als Eigentümer oder mit Berechtigungen).

Die von @kong bereitgestellte Abfrage kann zurückgegeben werden FALSE, die Tabelle kann jedoch weiterhin vorhanden sein. Es beantwortet die Frage:

Wie überprüfe ich, ob eine Tabelle (oder Ansicht) vorhanden ist und der aktuelle Benutzer Zugriff darauf hat?

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

Das Informationsschema ist hauptsächlich nützlich, um über Hauptversionen und über verschiedene RDBMS hinweg portabel zu bleiben. Die Implementierung ist jedoch langsam, da Postgres ausgefeilte Ansichten verwenden muss, um dem Standard zu entsprechen (dies information_schema.tablesist ein recht einfaches Beispiel). Und einige Informationen (wie OIDs) gehen bei der Übersetzung aus den Systemkatalogen verloren - die tatsächlich alle Informationen enthalten.

Systemkataloge

Ihre Frage war:

Wie überprüfe ich, ob eine Tabelle existiert?

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

Verwenden Sie die Systemkataloge pg_classund pg_namespacedirekt, was auch erheblich schneller ist. Jedoch pro Dokumentation aufpg_class :

Der Katalog pg_classkatalogisiert Tabellen und fast alles andere, was Spalten enthält oder auf andere Weise einer Tabelle ähnelt. Dies umfasst Indizes (siehe auch pg_index), Sequenzen , Ansichten , materialisierte Ansichten , zusammengesetzte Typen und TOAST-Tabellen .

Für diese spezielle Frage können Sie auch die Systemansicht verwendenpg_tables . Ein bisschen einfacher und portabler für alle wichtigen Postgres-Versionen (was für diese grundlegende Abfrage kaum von Belang ist):

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

Bezeichner müssen unter allen oben genannten Objekten eindeutig sein . Wenn Sie fragen möchten:

Wie kann überprüft werden, ob ein Name für eine Tabelle oder ein ähnliches Objekt in einem bestimmten Schema verwendet wird?

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: Besetzung zu regclass

SELECT 'schema_name.table_name'::regclass

Dies löst eine Ausnahme aus, wenn die (optional schemaqualifizierte) Tabelle (oder ein anderes Objekt, das diesen Namen belegt) nicht vorhanden ist.

Wenn Sie den Tabellennamen nicht schemaqualifizieren, wird regclassstandardmäßig eine Umwandlung in die zurückgegeben search_pathund die OID für die erste gefundene Tabelle zurückgegeben - oder eine Ausnahme, wenn sich die Tabelle in keinem der aufgelisteten Schemas befindet. Beachten Sie, dass die Systemschemata pg_catalogund pg_temp(das Schema für temporäre Objekte der aktuellen Sitzung) automatisch Teil von sind search_path.

Sie können das verwenden und eine mögliche Ausnahme in einer Funktion abfangen. Beispiel:

Eine Abfrage wie oben vermeidet mögliche Ausnahmen und ist daher etwas schneller.

to_regclass(rel_name) in Postgres 9.4+

Viel einfacher jetzt:

SELECT to_regclass('schema_name.table_name');

Wie die Besetzung, aber es kehrt zurück ...

... null anstatt einen Fehler auszulösen, wenn der Name nicht gefunden wird

Erwin Brandstetter
quelle
4
von der Schale:[[ `psql dbname -tAc "SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'ejabberd' AND table_name = 'users');"` = 't' ]]
Brauliobo
1
Gibt es einen Grund, warum Sie pg_tables nicht verwenden ?
m0meni
1
pg_tablesist eigentlich eine gute Idee für "Wie überprüfe ich, ob eine Tabelle existiert?" (Für die Überprüfung von Tabellen nur ., Nicht für andere Zwecke, wie oben erklärt sich auch, pg_tableseine Ansicht mit mehreren Tabellen ( pg_class, pg_namespace, pg_tablespace), das ist ein bisschen teurer Der wichtigste Grund:. Ich Abfrage verwendet bin pg_classdirekt und tat es nicht Denken Sie daran, pg_tableswenn Sie diese Antwort schreiben. Ich habe sie jetzt oben hinzugefügt, danke.
Erwin Brandstetter
1
@ sage88: Richtig, ich habe meinen falschen Kommentar entfernt. Sie können verwenden pg_my_temp_schema(), um die OID des tatsächlichen temporären Schemas abzurufen, falls vorhanden. (Aber Ansichten in der information_schemaenthalten keine OIDs. Sie könnten SELECT nspname FROM pg_namespace WHERE OID = pg_my_temp_schema()) Ihr Test hat mehrere Schwächen. Ein korrekter Test wäre table_schema LIKE 'pg\_temp\_%'oder strenger : table_schema ~ '^pg_temp_\d+$'.
Erwin Brandstetter
1
@PeterKrauss Sie erhalten diesen Fehler, wenn Sie versuchen, die Funktion to_regclass für eine Version von Postgres zu verwenden, die älter als 9.4 ist. Muss 9.4+ haben
spetz83
44

Verwenden Sie möglicherweise information_schema :

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

Für PostgreSQL 9.3 oder weniger ... Oder wer mag alle auf Text normalisiert

Drei Aromen meiner alten Bibliothek Swissknife: relname_exists(anyThing), relname_normalized(anyThing)und relnamechecked_to_array(anyThing). Alle Überprüfungen aus der Tabelle pg_catalog.pg_class und geben universelle Standarddatentypen ( Boolescher Wert , Text oder Text []) zurück.

/**
 * 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
quelle