zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    C++用于修饰的keyword
    UVa 884
    yii 使用 mongodb 小工具 YiiMongoDbSuite
    三种网络协议握手
    学习设计模式的前世今生
    B二分法
    链接链接新手变化需要注意哪些问题
    插值与拟合 课件链接
    UVa 740
    疯狂暑期学习计划~~~
  • 原文地址:https://www.cnblogs.com/yungiu/p/10983792.html
Copyright © 2011-2022 走看看