大聲的告訴我是不是被我標題中的兩個“之”給帶進來的??手動滑稽
需求
自己的一款軟件 GitHub 地址,關於PostgreSQL 已經設置成運行后自動創建序列,表和函數,但是數據庫還是要手動去創建,很不方便,想使用創建序列和表同樣的方法,去自動創建數據庫
過程
DDL 語句如下:
DB_NAME = """ CREATE DATABASE if not exists {}; ALTER DATABASE {} OWNER TO postgres; """.format(DATABASE_NAME, DATABASE_NAME)
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