Erstellen Sie einen Index, falls er nicht existiert

60

Ich arbeite an einer Funktion, mit der ich einen Index hinzufügen kann, wenn er nicht vorhanden ist. Ich habe das Problem, dass ich keine Liste der zu vergleichenden Indizes bekomme. Irgendwelche Gedanken?

Dies ist ein ähnliches Problem wie bei der Spaltenerstellung, das mit folgendem Code behoben wird:
https://stackoverflow.com/a/12603892/368511

GuidoS
quelle
Sie können Folgendes versuchen: SELECT * from pg_indexes where schemaname = '[schemaname]' and indexname = '[indexname]'. Ersetzen Sie [schemaname] und [indexname] durch die richtigen Werte. Ref: postgresql.org/docs/9.1/static/view-pg-indexes.html
jbarrameda

Antworten:

102

Indexnamen in PostgreSQL

  • Indexnamen sind in einem einzelnen Datenbankschema eindeutig.
  • Indexnamen dürfen nicht mit anderen Indizes, (Fremd-) Tabellen, (materialisierten) Ansichten, Sequenzen oder benutzerdefinierten zusammengesetzten Typen im selben Schema identisch sein.
  • Zwei Tabellen in demselben Schema dürfen keinen Index mit demselben Namen haben. (Folgt logisch.)

Wenn Sie sich nicht für den Namen des Indexes interessieren, lassen Sie Postgres diesen automatisch benennen:

CREATE INDEX ON tbl1 (col1);

ist (fast) dasselbe wie:

CREATE INDEX tbl1_col1_idx ON tbl1 USING btree (col1);

Mit der Ausnahme, dass Postgres Namenskollisionen vermeidet und automatisch den nächsten freien Namen auswählt:

tbl1_col1_idx 
tbl1_col1_idx2
tbl1_col1_idx3
...

Probier es einfach. Offensichtlich möchten Sie jedoch nicht mehrere redundante Indizes erstellen. Es wäre also keine gute Idee, einfach blindlings ein neues zu erstellen.

Auf Existenz prüfen

Postgres 9.3 oder älter

Eine sehr einfache Möglichkeit zum Testen besteht darin, den schemaqualifizierten Namen in Folgendes umzuwandeln regclass:

SELECT 'myschema.myname'::regclass;

Wenn es eine Ausnahme auslöst, ist der Name frei.
Oder, um dasselbe zu testen, ohne eine Ausnahme auszulösen, die in einer DOAnweisung verwendet wird:

DO
$$
BEGIN
   IF NOT EXISTS (
      SELECT
      FROM   pg_class c
      JOIN   pg_namespace n ON n.oid = c.relnamespace
      WHERE  c.relname = 'mytable_mycolumn_idx'
      AND    n.nspname = 'myschema'
   ) THEN

        CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
    END IF;
END
$$;

Dies funktioniert nicht CREATE INDEX CONCURRENTLY, da diese Variante nicht in eine äußere Transaktion eingebunden werden kann. Siehe Kommentar von @Gregory unten.

Die DOAussage wurde mit Postgres 9.0 eingeleitet. In früheren Versionen müssen Sie eine Funktion erstellen , um dasselbe zu tun.
Details dazu pg_classim Handbuch .
Grundlagen zu Indizes im Handbuch .

Postgres 9.4

Mit der neuen Funktion können Sie to_regclass()prüfen, ohne eine Ausnahme auszulösen:

DO
$$
BEGIN
   IF to_regclass('myschema.mytable_mycolumn_idx') IS NULL THEN
      CREATE INDEX mytable_mycolumn_idx ON myschema.mytable (mycolumn);
   END IF;

END
$$;

Gibt NULL zurück, wenn ein Index (oder ein anderes Objekt) dieses Namens nicht vorhanden ist. Sehen:

Postgres 9.5

Jetzt verfügbar:

CREATE INDEX IF NOT EXISTS ...

Das funktioniert auch für CREATE INDEX CONCURRENTLY IF NOT EXISTS.

Das Handbuch warnt jedoch :

Beachten Sie, dass es keine Garantie dafür gibt, dass der vorhandene Index dem entspricht, der erstellt worden wäre.

Es ist eine einfache Überprüfung des Objektnamens. Gilt für alle Varianten hier.

Erwin Brandstetter
quelle
7
Beachten Sie, dass Sie auf CONCURRENTLYdiese Weise keine Indizes hinzufügen können. Du wirst bekommen ERROR: CREATE INDEX CONCURRENTLY cannot be executed from a function or multi-command string.
Gregoltsov