JDBC를 통해 존재하지 않는 데이터베이스를 만들고 싶습니다. MySQL과 달리 PostgreSQL은 create if not exists
구문을 지원하지 않습니다 . 이를 수행하는 가장 좋은 방법은 무엇입니까?
응용 프로그램은 데이터베이스가 있는지 여부를 알지 못합니다. 데이터베이스가 존재하는지 확인하고 사용해야합니다. 따라서 원하는 데이터베이스에 연결하는 것이 합리적이며 데이터베이스가 존재하지 않아 연결이 실패하면 기본 postgres
데이터베이스 에 연결하여 새 데이터베이스를 만들어야 합니다. Postgres에서 반환 한 오류 코드를 확인했지만 동일한 종류의 관련 코드를 찾을 수 없습니다.
이를 달성하는 또 다른 방법은 postgres
데이터베이스 에 연결 하고 원하는 데이터베이스가 있는지 확인하고 그에 따라 조치를 취하는 것입니다. 두 번째는 운동하기가 조금 지루합니다.
Postgres에서이 기능을 달성하는 방법이 있습니까?
답변
제한
pg_database
동일한 데이터베이스 클러스터의 모든 데이터베이스에서 액세스 할 수 있는 시스템 카탈로그를 요청할 수 있습니다 . 까다로운 부분은 CREATE DATABASE
단일 문으로 만 실행할 수 있다는 것 입니다. 매뉴얼 :
CREATE DATABASE
트랜잭션 블록 내에서 실행할 수 없습니다.
따라서 DO
암시 적으로 트랜잭션 블록 내부에있는 함수 또는 문 내에서 직접 실행할 수 없습니다 .
(Postgres 11에 도입 된 SQL 프로시 저는 이것에도 도움이되지 않습니다 .)
psql 내에서 해결 방법
조건부로 DDL 문을 실행하여 psql 내에서이 문제를 해결할 수 있습니다.
SELECT 'CREATE DATABASE mydb'
WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec
\gexec
현재 쿼리 버퍼를 서버로 보낸 다음 쿼리 출력 (있는 경우)의 각 행에있는 각 열을 실행할 SQL 문으로 처리합니다.
셸에서 해결 방법
으로 \gexec
만 호출 psql의 필요 번 :
echo "SELECT 'CREATE DATABASE mydb' WHERE NOT EXISTS (SELECT FROM pg_database WHERE datname = 'mydb')\gexec" | psql
연결을 위해 더 많은 psql 옵션이 필요할 수 있습니다. 역할, 포트, 비밀번호, … 참조 :
psql 메타 명령이고 옵션 은 수동에 다음과 같은 단일 명령 을 기대 psql -c "SELECT ...\gexec"
하므로 동일한 것을 호출 할 수 없습니다 .\gexec
-c
command
서버에서 완전히 구문 분석 할 수있는 명령 문자열 (즉, psql 특정 기능이 포함되지 않음)이거나 단일 백 슬래시 명령이어야합니다. 따라서-c
옵션 내에서 SQL 및 psql 메타 명령을 혼합 할 수 없습니다 .
Postgres 트랜잭션 내에서 해결 방법
당신은 dblink
트랜잭션 블록 외부에서 실행되는 현재 데이터베이스에 연결을 . 따라서 효과도 롤백 할 수 없습니다.
이를 위해 추가 모듈 dblink를 설치합니다 (데이터베이스 당 한 번) :
그때:
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$;
다시 말하지만 연결을 위해 더 많은 psql 옵션이 필요할 수 있습니다. Ortwin의 추가 답변을 참조하십시오.
dblink에 대한 자세한 설명 :
반복 사용을위한 기능으로 만들 수 있습니다.
답변
또 다른 대안은 데이터베이스가 존재하지 않는 경우 데이터베이스를 생성하고 그렇지 않으면 그대로 유지하는 쉘 스크립트를 원할 경우입니다.
psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'my_db'" | grep -q 1 || psql -U postgres -c "CREATE DATABASE my_db"
동일한 인스턴스에서 여러 번 실행할 수있는 devops 프로비저닝 스크립트에이 기능이 도움이된다는 것을 알았습니다.
답변
@Erwin Brandstetter가 사용한 약간 확장 된 버전을 사용해야했습니다.
DO
$do$
DECLARE
_db TEXT := 'some_db';
_user TEXT := 'postgres_user';
_password TEXT := 'password';
BEGIN
CREATE EXTENSION IF NOT EXISTS dblink; -- enable extension
IF EXISTS (SELECT 1 FROM pg_database WHERE datname = _db) THEN
RAISE NOTICE 'Database already exists';
ELSE
PERFORM dblink_connect('host=localhost user=' || _user || ' password=' || _password || ' dbname=' || current_database());
PERFORM dblink_exec('CREATE DATABASE ' || _db);
END IF;
END
$do$
dblink
확장 기능 을 활성화하고 dblink에 대한 자격 증명을 제공해야했습니다. Postgres 9.4에서 작동합니다.
답변
데이터에 신경 쓰지 않는다면 먼저 데이터베이스를 삭제 한 다음 다시 만들 수 있습니다.
DROP DATABASE IF EXISTS dbname;
CREATE DATABASE dbname;
답변
PostgreSQL은 지원하지 않습니다 IF NOT EXISTS
에 대한 CREATE DATABASE
문. .NET에서만 지원됩니다 CREATE SCHEMA
. 또한 CREATE DATABASE
거래에서 발행 할 수 없으므로DO
예외 포착 블록에 .
언제 CREATE SCHEMA IF NOT EXISTS
발행 및 스키마가 이미 중복 객체 정보를 다음 예고 (안 오류)가 존재한다 발생합니다.
이러한 문제를 해결하려면 dblink
데이터베이스 서버에 대한 새 연결을 열고 트랜잭션에 들어 가지 않고 쿼리를 실행하는 확장 을 사용해야 합니다. 빈 문자열을 제공하여 연결 매개 변수를 재사용 할 수 있습니다.
다음은에서 와 같은 동작으로 PL/pgSQL
완전히 시뮬레이션하는 코드 CREATE DATABASE IF NOT EXISTS
입니다 CREATE SCHEMA IF NOT EXISTS
. , catch 예외 (데이터베이스가 이미 존재하는 경우 발행 됨) CREATE DATABASE
를 통해 호출 하고 전파하는 알림으로 변환합니다 . 문자열 메시지는 같은 방식으로 추가 되었습니다 .dblink
duplicate_database
errcode
, skipping
CREATE SCHEMA IF NOT EXISTS
CREATE EXTENSION IF NOT EXISTS dblink;
DO $$
BEGIN
PERFORM dblink_exec('', 'CREATE DATABASE testdb');
EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
END
$$;
이 솔루션은 데이터베이스가 존재하는지 확인하고 자체 생성 사이에 외부 프로세스 (또는 동일한 스크립트의 다른 인스턴스)에 의해 데이터베이스를 생성 할 수있는 다른 답변과 같이 경쟁 조건이 없습니다.
또한 CREATE DATABASE
데이터베이스 이외의 다른 오류로 인해 실패하면이 오류가 오류로 전파되고 자동으로 삭제되지 않습니다. duplicate_database
오류 만 포착 할 수 있습니다. 그래서 그것은 정말로 IF NOT EXISTS
해야하는 대로 작동 합니다.
이 코드를 자체 함수에 넣거나 직접 호출하거나 트랜잭션에서 호출 할 수 있습니다. 롤백 (삭제 된 데이터베이스 복원)은 작동하지 않습니다.
출력 테스트 (DO를 통해 두 번 호출 한 다음 직접 호출) :
$ sudo -u postgres psql
psql (9.6.12)
Type "help" for help.
postgres=# \set ON_ERROR_STOP on
postgres=# \set VERBOSITY verbose
postgres=#
postgres=# CREATE EXTENSION IF NOT EXISTS dblink;
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
DO
postgres=#
postgres=# CREATE EXTENSION IF NOT EXISTS dblink;
NOTICE: 42710: extension "dblink" already exists, skipping
LOCATION: CreateExtension, extension.c:1539
CREATE EXTENSION
postgres=# DO $$
postgres$# BEGIN
postgres$# PERFORM dblink_exec('', 'CREATE DATABASE testdb');
postgres$# EXCEPTION WHEN duplicate_database THEN RAISE NOTICE '%, skipping', SQLERRM USING ERRCODE = SQLSTATE;
postgres$# END
postgres$# $$;
NOTICE: 42P04: database "testdb" already exists, skipping
LOCATION: exec_stmt_raise, pl_exec.c:3165
DO
postgres=#
postgres=# CREATE DATABASE testdb;
ERROR: 42P04: database "testdb" already exists
LOCATION: createdb, dbcommands.c:467
답변
쉘을 사용할 수 있다면
psql -U postgres -c 'select 1' -d $DB &>dev/null || psql -U postgres -tc 'create database $DB'
나는 psql -U postgres -c "select 1" -d $DB
더 쉽다고 생각 SELECT 1 FROM pg_database WHERE datname = 'my_db'
하고 결합하기 쉬운 한 가지 유형의 견적 만 필요합니다.sh -c
합니다.
나는 내 ansible 작업에서 이것을 사용합니다.
- name: create service database
shell: docker exec postgres sh -c '{ psql -U postgres -tc "SELECT 1" -d {{service_name}} &> /dev/null && echo -n 1; } || { psql -U postgres -c "CREATE DATABASE {{service_name}}"}'
register: shell_result
changed_when: "shell_result.stdout != '1'"
답변
createdb
CLI 도구를 사용하여 데이터베이스를 만드십시오 .
PGHOST="my.database.domain.com"
PGUSER="postgres"
PGDB="mydb"
createdb -h $PGHOST -p $PGPORT -U $PGUSER $PGDB
데이터베이스가있는 경우 오류를 반환합니다.
createdb: database creation failed: ERROR: database "mydb" already exists