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

原文地址:https://www.cnblogs.com/yungiu/p/10983792.html