PostgreSQL 學習之使用psycopg2 操作之數據庫不存在才創建


大聲的告訴我是不是被我標題中的兩個“之”給帶進來的??手動滑稽

需求

自己的一款軟件 GitHub 地址,關於PostgreSQL 已經設置成運行后自動創建序列,表和函數,但是數據庫還是要手動去創建,很不方便,想使用創建序列和表同樣的方法,去自動創建數據庫

過程

DDL 語句如下:

DB_NAME = """
CREATE DATABASE if not exists {};

ALTER DATABASE {} OWNER TO postgres;
""".format(DATABASE_NAME, DATABASE_NAME)
發現會報一個DDL 語句語法錯誤
psycopg2.ProgrammingError: syntax error at or near "not"
LINE 2: CREATE DATABASE if not exists test_classs;

創建序列和表的時候都沒問題啊(函數我使用的是先刪除,后創建),這怎么報錯了?本着先運行成功,再實現功能的原則,我把“if not exists”給去掉,結果還是報錯:

psycopg2.InternalError: CREATE DATABASE cannot run inside a transaction block

不能在事務塊中創建數據庫,大概意思就是這樣不安全吧,百度加谷歌,有兩種方法:

1.在 psycopg2 extensions 里使用 ISOLATION_LEVEL_AUTOCOMMIT,原理就是讓連接發出命令時不啟動任何事務,看常量名字,字面意思也是自動提交,並且不需要commit()或rollback()如:

import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT # <-- ADD THIS LINE

con = psycopg2.connect(...)

con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT) # <-- ADD THIS LINE

cur = con.cursor()
cur.execute("CREATE DATABASE %s  ;" % self.db_name)
cur.close()
con.close()

2.讓連接處於自動提交模式,使用連接對象的autocommit 屬性,如:

import psycopg2

con = psycopg2.connect(...)
con.autocommit = True

cur = con.cursor()
cur.execute('CREATE DATABASE {};'.format(db_name))
cur.close()
# 如果連接不關閉,則需要改回連接對象的autocommit 屬性
con.autocommit = False
# 如果連接關閉,則不需要執行上面這行代碼
# con.close()

但是當我運行的時候,還是遇到了另外一個錯誤:

psycopg2.InternalError: CREATE DATABASE cannot be executed from a function or multi-command string

從字面意思上看,是不能在函數中創建數據庫,且不能在多命令的字符串中創建數據庫,函數我這里沒用,只能是多行命令了,看了一下自己的DDL 語句,發現自己復制創建表的DDL 語句的部分有點多了。。從最簡單的做起吧,畢竟上面的這兩個例子都沒有問題,將DDL 語句改為:

DB_NAME = """
CREATE DATABASE {};
""".format(DATABASE_NAME)

再次運行,雖然有一丟丟慢,但是還是創建成功了,但是在這里,我建議使用第二種方法,因為簡單,不用再導入這個很長的常量,而且控制還很方便,比如下面這個需求,創建第一個數據庫,創建第二個數據庫(怎么會有這么奇葩的需求,哈哈,都是為了舉例方便)

import psycopg2
from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT,ISOLATION_LEVEL_DEFAULT

DATABASE_NAME = 'test_classs'
DATABASE_NAME2 = 'test_classssssss'

DB_NAME = """
CREATE DATABASE {};
""".format(DATABASE_NAME)

DB_NAME2 = """
CREATE DATABASE {};
""".format(DATABASE_NAME2)

conn = psycopg2.connect(...)
conn.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
cur = conn.cursor()
cur.execute(DB_NAME)
conn.set_isolation_level(ISOLATION_LEVEL_DEFAULT)
cur.execute(DB_NAME2)
cur.close()
conn.close()

看到了吧,你需要導入兩個很長很長的變量名,當然,在IDE 的提示下,這樣其實也不費事,但是我總感覺這樣不怎么優雅,畢竟是Python,不優雅怎么行?所以我們可以使用另外一種方式:

import psycopg2

DATABASE_NAME = 'test_classs'
DATABASE_NAME2 = 'test_classssssss'

DB_NAME = """
CREATE DATABASE {};
""".format(DATABASE_NAME)

DB_NAME2 = """
CREATE DATABASE {};
""".format(DATABASE_NAME2)

conn = psycopg2.connect(...)
conn.autocommit = True
cur = conn.cursor()
cur.execute(DB_NAME)
conn.autocommit = False
cur.execute(DB_NAME2)
cur.close()
conn.close()

效果是一樣的,看着是不是舒服很多了?

好吧,現在我們的數據庫是可以創建了,那么怎么樣實現如果不存在就創建,存在就不處理呢?

既然不能像‘MySQL’那樣用‘if not exists’來創建數據庫,那么只能換另外一種思路了,先查詢看看這個數據庫是否存在,如果不存在就設置自動提交然后創建,如果存在,就不做處理,excuse me?這是換思路了嗎?咳咳。。好吧,思路沒變,只是過程用代碼實現了而已。。具體代碼如下:

import psycopg2


DATABASE_NAME = 'test_classss'
DB_NAME_EXIST = """
select * from pg_database where datname='{}';
""".format(DATABASE_NAME)
DB_NAME = """
CREATE DATABASE {};
""".format(DATABASE_NAME)
conn = psycopg2.connect(...)
cur = conn.cursor()
cur.execute(DB_NAME_EXIST)
result = cur.fetchall()
if result == []:
    conn.autocommit = True
    cur.execute(DB_NAME)
    conn.autocommit = False
else:
    print('{} is exist'.format(DATABASE_NAME))
cur.close()
conn.close()

 

注意,ALL_DB_NAME 這條SQL 語句的條件后面占位符{}必須用引號引起來,否則會報 column "test_class" does not exist 的錯誤

測試了一下,貌似沒有問題,那么改用一個不存在的數據庫看看,果然,沒有我想的那么簡單。。

    conn.autocommit = True
psycopg2.ProgrammingError: set_session cannot be used inside a transaction

百度了一下,也沒找到答案,不過根據字面意思,肯定是和事務有關,先嘗試了一個最笨的方法,就是查詢后記錄查詢結果,然后把游標和連接都關閉,然后再創建新的連接,新的游標,根據結果去創建數據庫,或者不處理,發現沒有問題,雖然感覺怪怪的,但是也蠻開心的,就在要往博客園上記下解決方法的時候,忽然想到了,把上次的查詢后的給提交一下不就OK 了?快被自己蠢哭了,這也是為什么我打了那么多字,而不直接上代碼的原因,這是經驗的不足,也是數據庫知識的不足,臉紅,直接上代碼吧,其實就加一行代碼就解決了。

結果

 

import psycopg2


DATABASE_NAME = 'test_classss'
DB_NAME_EXIST = """
select * from pg_database where datname='{}';
""".format(DATABASE_NAME)
DB_NAME = """
CREATE DATABASE {};
""".format(DATABASE_NAME)
conn = psycopg2.connect(...)
cur = conn.cursor()
cur.execute(DB_NAME_EXIST)
conn.commit()  # <-- ADD THIS LINE
result = cur.fetchall()
if result == []:
    conn.autocommit = True
    cur.execute(DB_NAME)
    conn.autocommit = False
else:
    print('{} is exist'.format(DATABASE_NAME))
cur.close()
conn.close()

 

 

 

行吧,到此位置,功能實現~我們下次再見,如果你覺得對你有幫助,麻煩點個“推薦”,謝謝~

當然,如果你有更優雅的辦法,請在下面留言,謝謝

 

參考

使用python創建Postgres數據庫:https://www.e-learn.cn/content/wangluowenzhang/448331

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM