Ograniczenia
Możesz zapytać o katalog systemowy pg_database
- dostępny z dowolnej bazy danych w tym samym klastrze baz danych. Trudność polega na tym, że CREATE DATABASE
można ją wykonać tylko jako pojedynczą instrukcję. Instrukcja:
CREATE DATABASE
nie można wykonać wewnątrz bloku transakcji.
Dlatego nie można go uruchomić bezpośrednio w funkcji lub DO
instrukcji, gdzie byłby niejawnie wewnątrz bloku transakcji.
(Procedury SQL, wprowadzone w Postgres 11, również nie mogą w tym pomóc ).
Obejście z poziomu psql
Możesz obejść to z poziomu psql, wykonując instrukcję DDL warunkowo:
SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec
Instrukcja:
\gexec
Wysyła bieżący bufor zapytania do serwera, a następnie traktuje każdą kolumnę każdego wiersza wyniku zapytania (jeśli istnieje) jako instrukcję SQL do wykonania.
Obejście problemu z powłoki
Z \gexec
musisz tylko raz wywołać psql :
echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql
Możesz potrzebować więcej opcji psql do połączenia; rola, port, hasło, ... Zobacz:
Tego samego nie można wywołać za pomocą, psql -c "SELECT ...\gexec"
ponieważ \gexec
jest to meta-polecenie psql, a -c
opcja oczekuje pojedynczego polecenia, dla którego podręcznik stwierdza:
command
musi być albo ciągiem polecenia, który jest całkowicie analizowalny przez serwer (tj. nie zawiera cech charakterystycznych dla psql), albo pojedynczym poleceniem z ukośnikiem odwrotnym. Dlatego nie można łączyć meta-poleceń SQL i psql w ramach -c
opcji.
Obejście problemu z transakcji Postgres
Możesz użyć dblink
połączenia z powrotem do bieżącej bazy danych, która działa poza blokiem transakcji. Dlatego też efektów nie można cofnąć.
W tym celu zainstaluj dodatkowy moduł dblink (raz na bazę danych):
Następnie:
DO
$do$
BEGIN
IF EXISTS (SELECT FROM pg_database WHERE datname = 'mydb') THEN
RAISE NOTICE 'Database already exists'; -- optional
ELSE
PERFORM dblink_exec('dbname=' || current_database() -- current db
, 'CREATE DATABASE mydb');
END IF;
END
$do$;
Ponownie, możesz potrzebować więcej opcji psql do połączenia. Zobacz dodatkową odpowiedź Ortwina:
Szczegółowe wyjaśnienie dblink:
Możesz ustawić tę funkcję jako funkcję wielokrotnego użytku.
dblink_connect
.\gexec
kiedy uruchomiłem pierwsze zapytanie z powłoki, ale zadziałało.inna alternatywa, na wypadek gdybyś chciał mieć skrypt powłoki, który tworzy bazę danych, jeśli ona nie istnieje, a poza tym po prostu zachowuje ją taką, jaka jest:
Zauważyłem, że jest to pomocne w skryptach aprowizacji DevOps, które możesz chcieć uruchamiać wiele razy w tej samej instancji.
źródło
c:\Program Files\PostgreSQL\9.6\bin $ psql.exe -U admin -tc "SELECT 1 FROM pg_database WHERE datname = 'my_db'" | grep -q 1 || psql -U admin -c "CREATE DATABASE my_db" 'grep' is not recognized as an internal or external command, operable program or batch file.
Co zrobiłem źle ?grep
na swojej drodze. W systemie Windowsgrep
nie jest instalowany domyślnie. Możesz wyszukaćgnu grep windows
wersję, która może działać w systemie Windows.Musiałem użyć nieco rozszerzonej wersji @Erwin Brandstetter używanej:
Musiałem włączyć
dblink
rozszerzenie, a ponadto musiałem podać poświadczenia dla dblink. Działa z Postgres 9.4.źródło
Jeśli nie dbasz o dane, możesz najpierw usunąć bazę danych, a następnie utworzyć ją ponownie:
źródło
PostgreSQL nie obsługuje
IF NOT EXISTS
doCREATE DATABASE
rachunku. Jest obsługiwany tylko wCREATE SCHEMA
. PonadtoCREATE DATABASE
nie może być wystawiony w transakcji, dlatego nie może być wDO
bloku z przechwytywaniem wyjątków.Gdy
CREATE SCHEMA IF NOT EXISTS
zostanie wydany, a schemat już istnieje, zgłaszane jest powiadomienie (nie błąd) ze zduplikowanymi informacjami o obiekcie.Aby rozwiązać te problemy, należy użyć
dblink
rozszerzenia, które otwiera nowe połączenie z serwerem bazy danych i wykonuje zapytanie bez wchodzenia w transakcję. Możesz ponownie użyć parametrów połączenia, podając pusty ciąg.Poniżej znajduje się
PL/pgSQL
kod, który w pełni symulujeCREATE DATABASE IF NOT EXISTS
z takim samym zachowaniem jak wCREATE SCHEMA IF NOT EXISTS
. WywołujeCREATE DATABASE
przezdblink
, catchduplicate_database
wyjątek (który jest wystawiany, gdy baza danych już istnieje) i przekształca go w powiadomienie z propagacjąerrcode
. Wiadomość tekstowa została dołączona, skipping
w taki sam sposób, jak to się dziejeCREATE SCHEMA IF NOT EXISTS
.To rozwiązanie nie ma wyścigu, jak w innych odpowiedziach, gdzie baza danych może być utworzona przez proces zewnętrzny (lub inną instancję tego samego skryptu) pomiędzy sprawdzeniem, czy baza istnieje, a jej własnym utworzeniem.
Co więcej, gdy
CREATE DATABASE
nie powiedzie się z innym błędem niż baza danych już istnieje, ten błąd jest propagowany jako błąd i nie jest dyskretnie odrzucany. Jest tylko haczyk naduplicate_database
błąd. Więc naprawdę zachowuje się tak, jakIF NOT EXISTS
powinien.Możesz umieścić ten kod we własnej funkcji, wywołać go bezpośrednio lub z transakcji. Samo wycofanie (przywrócenie usuniętej bazy danych) nie zadziała.
Wyjście testowe (wywoływane dwa razy przez DO, a następnie bezpośrednio):
źródło
Jeśli możesz użyć powłoki, spróbuj
Myślę, że
psql -U postgres -c "select 1" -d $DB
jest łatwiejszy niżSELECT 1 FROM pg_database WHERE datname = 'my_db'
i potrzebuje tylko jednego rodzaju cytatu, łatwiejszego do połączeniash -c
.Używam tego w moim zadaniu ansibla
źródło
Wystarczy stworzyć bazę danych za pomocą
createdb
narzędzia CLI:Jeśli baza danych istnieje, zwróci błąd:
źródło
Zaktualizuj do PostgreSQL 9.5 lub nowszej. Jeśli (nie) istnieje, zostało wprowadzone w wersji 9.5.
źródło
if not exists
dlaCREATE DATABASE
- nawet w Postgres 11 postgresql.org/docs/current/static/sql-createdatabase.html