Utwórz indeks, jeśli nie istnieje

60

Pracuję nad funkcją, która pozwala mi dodać indeks, jeśli nie istnieje. Mam problem z tym, że nie mogę uzyskać listy indeksów do porównania. jakieś pomysły?

Jest to podobny problem do tworzenia kolumny, który został rozwiązany za pomocą tego kodu:
https://stackoverflow.com/a/12603892/368511

GuidoS
źródło
możesz spróbować: WYBIERZ * z pg_indexes gdzie schemaname = '[schemaname]' i indexname = '[indexname]'. Zamień [nazwa schematu] i [nazwa indeksu] na prawidłowe wartości. Ref: postgresql.org/docs/9.1/static/view-pg-indexes.html
jbarrameda

Odpowiedzi:

102

Nazwy indeksów w PostgreSQL

  • Nazwy indeksów są unikalne w jednym schemacie bazy danych.
  • Nazwy indeksów nie mogą być takie same jak jakikolwiek inny indeks, (obca) tabela, (zmaterializowany) widok, sekwencja lub typ złożony zdefiniowany przez użytkownika w tym samym schemacie.
  • Dwie tabele w tym samym schemacie nie mogą mieć indeksu o tej samej nazwie. (Podąża logicznie.)

Jeśli nie obchodzi Cię nazwa indeksu, poproś o automatyczne nadanie mu nazwy Postgres:

CREATE INDEX ON tbl1 (col1);

jest (prawie) taki sam jak:

CREATE INDEX tbl1_col1_idx ON tbl1 USING btree (col1);

Tyle że Postgres uniknie kolizji nazw i automatycznie wybierze następną darmową nazwę:

tbl1_col1_idx 
tbl1_col1_idx2
tbl1_col1_idx3
...

Po prostu spróbuj. Ale oczywiście nie chcesz tworzyć wielu nadmiarowych indeksów. Więc nie byłoby dobrym pomysłem po prostu tworzenie na ślepo nowego.

Test na istnienie

Postgres 9.3 lub starszy

Bardzo prostym sposobem na przetestowanie jest rzutowanie nazwy kwalifikowanej do schematu na regclass:

SELECT 'myschema.myname'::regclass;

Jeśli zgłosi wyjątek, nazwa jest bezpłatna.
Lub, aby przetestować to samo bez zgłaszania wyjątku, użyte w DOinstrukcji:

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

To nie działa CREATE INDEX CONCURRENTLY, ponieważ ten wariant nie może być zawarty w zewnętrznej transakcji. Zobacz komentarz @Gregory poniżej.

DOOświadczenie zostało wprowadzone z PostgreSQL 9.0. We wcześniejszych wersjach musisz utworzyć funkcję, aby zrobić to samo.
Szczegóły na temat pg_classinstrukcji .
Podstawy indeksów w instrukcji .

Postgres 9.4

Możesz użyć nowej funkcji to_regclass()do sprawdzenia bez zgłaszania wyjątku:

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

END
$$;

Zwraca NULL, jeśli indeks (lub inny obiekt) o tej nazwie nie istnieje. Widzieć:

Postgres 9.5

Już dostępny:

CREATE INDEX IF NOT EXISTS ...

To też działa CREATE INDEX CONCURRENTLY IF NOT EXISTS.

Jednak instrukcja ostrzega :

Należy pamiętać, że nie ma gwarancji, że istniejący indeks jest podobny do tego, który zostałby utworzony.

Jest to zwykłe sprawdzenie nazwy obiektu. Dotyczy wszystkich wariantów tutaj.

Erwin Brandstetter
źródło
7
Będąc świetną odpowiedzią, pamiętaj, że nie można dodawać indeksów w CONCURRENTLYten sposób. Dostaniesz ERROR: CREATE INDEX CONCURRENTLY cannot be executed from a function or multi-command string.
gregoltsov