PostgreSQL के लिए यदि नहीं, तो क्या करें


115

मैं एक डेटाबेस बनाना चाहता हूं जो JDBC के माध्यम से मौजूद नहीं है। MySQL के विपरीत, PostgreSQL create if not existsसिंटैक्स का समर्थन नहीं करता है। इसे निष्पादित करने का श्रेष्ठ तरीका क्या है?

एप्लिकेशन को पता नहीं है कि डेटाबेस मौजूद है या नहीं। इसे जांचना चाहिए और यदि डेटाबेस मौजूद है तो इसका उपयोग किया जाना चाहिए। तो यह वांछित डेटाबेस से कनेक्ट करने के लिए समझ में आता है और अगर डेटाबेस के गैर-मौजूद होने के कारण कनेक्शन विफल हो जाता है तो इसे नया डेटाबेस (डिफ़ॉल्ट postgresडेटाबेस से कनेक्ट करके ) बनाना चाहिए । मैंने पोस्टग्रेज द्वारा लौटाए गए त्रुटि कोड की जांच की लेकिन मुझे कोई प्रासंगिक कोड नहीं मिला जो कि प्रजाति समान हो।

इसे प्राप्त करने का एक अन्य तरीका postgresडेटाबेस से जुड़ना और यह जांचना होगा कि वांछित डेटाबेस मौजूद है और तदनुसार कार्रवाई करें। दूसरा काम करने के लिए थोड़ा थकाऊ है।

क्या Postgres में इस कार्यक्षमता को प्राप्त करने का कोई तरीका है?

जवाबों:


111

प्रतिबंध

आप सिस्टम कैटलॉग से पूछ सकते हैं pg_database- एक ही डेटाबेस क्लस्टर में किसी भी डेटाबेस से सुलभ। मुश्किल हिस्सा यह है कि CREATE DATABASEकेवल एक बयान के रूप में निष्पादित किया जा सकता है। नियम पुस्तिका:

CREATE DATABASE लेन-देन ब्लॉक के अंदर निष्पादित नहीं किया जा सकता है।

तो इसे सीधे किसी फ़ंक्शन या DOस्टेटमेंट के अंदर नहीं चलाया जा सकता है , जहां यह एक लेनदेन ब्लॉक के अंदर होगा।

(एसक्यूएल प्रक्रियाएं, पोस्टग्रेज 11 के साथ शुरू की गईं, इसके साथ भी मदद नहीं कर सकती हैं ।)

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 meta कमांड के psql -c "SELECT ...\gexec"बाद से इसे नहीं बुलाया जा सकता \gexecहै और -cविकल्प एक एकल कमांड की अपेक्षा करता है जिसके लिए मैनुअल कहता है:

commandया तो एक कमांड स्ट्रिंग होना चाहिए जो सर्वर द्वारा पूरी तरह से पार्स करने योग्य हो (यानी, इसमें कोई psql- विशिष्ट सुविधाएं नहीं हैं), या एक एकल बैकस्लैश कमांड। इस प्रकार आप SQL और psql मेटा-कमांड को एक -cविकल्प के भीतर नहीं मिला सकते हैं ।

पोस्टग्रेज लेनदेन के भीतर से समाधान

आप 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 विकल्प की आवश्यकता हो सकती है। देखें ऑर्टविन का जोड़ा गया जवाब:

डब्लिंक के लिए विस्तृत विवरण:

आप इसे बार-बार उपयोग के लिए एक फ़ंक्शन बना सकते हैं।


दूरस्थ से AWS RDS Postgres पर एक डेटाबेस बनाते समय मैं इसके साथ समस्या में आया था। RDS मास्टर उपयोगकर्ता सुपर उपयोगकर्ता नहीं है और इसलिए इसका उपयोग करने की अनुमति नहीं है dblink_connect
ओन्ड्रेज बर्कर्ट

यदि आपके पास सुपरयुसर विशेषाधिकार नहीं हैं, तो आप कनेक्शन के लिए पासवर्ड का उपयोग कर सकते हैं। विवरण: dba.stackexchange.com/a/105186/3684
Erwin Brandstetter

एक आकर्षण की तरह काम किया, डॉकटर कंटेनर के अंदर एक init.sql स्क्रिप्ट के भीतर इस्तेमाल किया। धन्यवाद!
माइकल जे। रॉबर्ट्स

मुझे \gexecशेल से पहली क्वेरी चलाते समय छोड़ना पड़ा , लेकिन यह काम कर गया।
FilBot3

117

एक अन्य विकल्प, बस अगर आप एक शेल स्क्रिप्ट चाहते हैं जो डेटाबेस बनाता है अगर यह मौजूद नहीं है और अन्यथा बस इसे उसी रूप में रखता है:

psql -U postgres -tc "SELECT 1 FROM pg_database WHERE datname = 'my_db'" | grep -q 1 || psql -U postgres -c "CREATE DATABASE my_db"

मैंने इसे लिपियों को प्रोविज़न करने में मददगार पाया, जिसे आप एक ही उदाहरण पर कई बार चलाना चाहें।


यह मेरे लिए काम नहीं करता है। 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.मैंने गलत क्या किया ?
एंटोन एनीकेव

2
आप grepअपने रास्ते में नहीं है । विंडोज पर, grepडिफ़ॉल्ट रूप से इंस्टॉल नहीं किया गया है। आप gnu grep windowsएक ऐसे संस्करण को खोज सकते हैं जो विंडोज पर काम कर सके।
रॉड

Thx @Rod। जब मैंने grep स्थापित किया तो इस स्क्रिप्ट ने मेरे लिए काम किया।
एंटोन एनीकेव

@AntonAnikeev: बिना grep के एक एकल कॉल के साथ किया जा सकता है। मैंने अपने उत्तर के समाधान जोड़े।
इरविन ब्रान्डेसटेटर

1
मुझे लगता है कि पहले यह पता लगाना उपयोगी है कि एक कनेक्शन संभव है; यदि कोई कनेक्शन उपलब्ध नहीं है (गलत होस्टनाम, नेटवर्क डाउन आदि), तो स्क्रिप्ट डेटाबेस बनाने का प्रयास करेगी और संभवतः भ्रामक त्रुटि संदेश के साथ विफल हो जाएगी
ओलिवर

8

मुझे थोड़ा विस्तारित संस्करण का उपयोग करना पड़ा @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एक्सटेंशन सक्षम करना था , साथ ही मुझे डब्लिंक के लिए क्रेडेंशियल्स प्रदान करना था। पोस्टग्रैस के साथ काम करता है 9.4।


7

यदि आपको डेटा की परवाह नहीं है, तो आप पहले डेटाबेस को छोड़ सकते हैं और फिर इसे फिर से बना सकते हैं:

DROP DATABASE IF EXISTS dbname;
CREATE DATABASE dbname;

बहुत सुंदर समाधान। यदि आप डेटा का ध्यान रखते हैं तो बस डेटाबेस का बैकअप लेना न भूलें । परिस्थितियों के परीक्षण के लिए हालांकि यह मेरा पसंदीदा समाधान है।
लैरीक्स डेसीडुआ

6

PostgreSQL का समर्थन नहीं करता IF NOT EXISTSके लिए CREATE DATABASEबयान। यह केवल में समर्थित है CREATE SCHEMA। इसके अलावा CREATE DATABASEलेनदेन में जारी नहीं किया जा सकता है इसलिए यह DOअपवाद पकड़ने के साथ ब्लॉक में नहीं हो सकता है ।

जब CREATE SCHEMA IF NOT EXISTSजारी किया जाता है और स्कीमा पहले से मौजूद होता है तो डुप्लिकेट ऑब्जेक्ट जानकारी के साथ नोटिस (त्रुटि नहीं) उठाया जाता है।

इन समस्याओं को हल करने के लिए आपको dblinkएक्सटेंशन का उपयोग करना होगा जो डेटाबेस सर्वर के लिए एक नया कनेक्शन खोलता है और लेनदेन में प्रवेश किए बिना क्वेरी निष्पादित करता है। आप खाली स्ट्रिंग की आपूर्ति के साथ कनेक्शन मापदंडों का पुन: उपयोग कर सकते हैं।

नीचे PL/pgSQLकोड है जो पूरी तरह CREATE DATABASE IF NOT EXISTSसे समान व्यवहार के साथ अनुकरण करता है CREATE SCHEMA IF NOT EXISTS। यह कॉल के 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करना चाहिए।

आप इस कोड को अपने कार्य में लगा सकते हैं, इसे सीधे या लेनदेन से बुला सकते हैं। बस रोलबैक (गिरा हुआ डेटाबेस पुनर्स्थापित करें) काम नहीं करेगा।

परीक्षण उत्पादन (दो बार के माध्यम से और फिर सीधे कहा जाता है):

$ 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

1
यह वर्तमान में यहां एकमात्र सही उत्तर है, जो दौड़ की स्थिति से ग्रस्त नहीं है, और आवश्यक चयनात्मक त्रुटि से निपटने का उपयोग करता है। यह वास्तव में अफ़सोस की बात है कि यह उत्तर (पूरी तरह से सही नहीं) के बाद दिखाई दिया शीर्ष उत्तर ने अधिक 70 अंक एकत्र किए।
वोग

2
खैर, अन्य उत्तर सभी संभावित कोने के मामलों को संभालने के लिए ऐसे सटीक नहीं हैं जो हो सकते हैं। आप मेरे पीएल / pgSQL कोड को समानांतर में अधिक बार कॉल कर सकते हैं और यह विफल नहीं होता है।
पाली

1

यदि आप शेल का उपयोग कर सकते हैं, तो प्रयास करें

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

मैं इसका उपयोग अपने काम में करता हूं

- 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'"

0

बस createdbCLI टूल का उपयोग करके डेटाबेस बनाएं :

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.