【问题标题】:SQL ALTER and DROP database IF EXISTS with PYODBCSQL ALTER 和 DROP 数据库 IF EXISTS with PYODBC
【发布时间】:2020-03-19 20:40:00
【问题描述】:

作为一个 SQL 新手,我试图确保最初将数据导入数据库时​​不会创建重复条目,并且将以编程方式创建数据库。如果有更有效的方法来执行此操作(请告诉我!),我不会感到惊讶,但我的方法是删除数据库并在它已经存在的情况下重新创建它,据我所知,这非常快。我已经在函数之外成功地使用了相同的语句并且没有格式化字符串,但是在创建一个函数来执行它时,我通过 PYODBC 收到错误:

ProgrammingError: ('42S22', "[42S22] [Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Invalid column name 'test'. (207) (SQLExecDirectW)")

这很令人困惑,因为我不想引用任何列,更不用说表了;所以这使得故障排除变得困难。功能如下:

def db_connect(db, driver='ODBC Driver 17 for SQL Server', host='', UID='', PWD='', autocommit=False):
    """Returns a connection and a cursor object for the specified database."""
    conn = pyodbc.connect(driver=driver,
                        host=host,
                        database=db,
                        UID=UID,
                        PWD=PWD,
                        autocommit=autocommit
                        )
    print(f'Connect established to database {db}')
    return conn, conn.cursor()

def db_clear(db, recreate=True):
    """Drops and recreates the specified database, ready for insert."""
    conn, curs = db_connect('master')
    curs.execute(f"""IF EXISTS (SELECT name from sys.databases WHERE (name = {db}))
        BEGIN
            ALTER DATABASE {db} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
            DROP DATABASE {db};
        END;""")
    if recreate:
        curs.execute(f"CREATE DATABASE {db};")
        conn.close()
        print(f'{db} successfully dropped and recreated.')
    else:
        print(f'{db} successfully dropped.')
    return

db_clear('test')

在包含END;""") 的行上引发异常。工作版本(不包含在函数中)和这个函数版本之间只有两个区别,因为我开始使用更新的驱动程序来更好地处理数据类型转换,并且在这些函数完成后我关闭了自动提交以批量插入他们的工作。我尝试将这两个选项都恢复为函数中的原始设置,但收到了相同的错误。任何帮助表示赞赏!

【问题讨论】:

    标签: python sql pyodbc


    【解决方案1】:

    你的字符串格式

    f"""IF EXISTS (SELECT name from sys.databases WHERE (name = {db}))
        BEGIN
            ALTER DATABASE {db} SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
            DROP DATABASE {db};
        END;"""
    

    正在生产

    IF EXISTS (SELECT name from sys.databases WHERE (name = test))
    BEGIN
        ALTER DATABASE test SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
        DROP DATABASE test;
    END;
    

    当 SQL 解析器看到 WHERE (name = test) 时,它会将 test 解释为列名(在 sys.databases 表/视图中),就像它(正确地)将 name 解释为列名一样。

    相反,您要做的是向 WHERE 子句提供一个 参数值

    sql = f"""IF EXISTS (SELECT name from sys.databases WHERE (name = ?))
              BEGIN
                  ALTER DATABASE [{db}] SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
                  DROP DATABASE [{db}];
              END;"""
    curs.execute(sql, db)
    

    【讨论】:

    • 谢谢!我对声明 cursor_obj.executemany(f"""INSERT INTO {table_name} ({columns}) VALUES ({parameter_placeholders});""", subset) 有类似的问题,其中子集是元组(行)的列表。有没有更有效的方法来参数化 {table_name} 而无需复制列表中的每个元组并将 table_name 作为第一个参数添加?我无法找到有关 PYODBC 参数的这些详细信息。
    • 使用 f-string 创建 SQL 命令文本,例如 INSERT INTO my_table (col_a, col_b) VALUES (?, ?),然后将该命令文本与仅包含列 values 的元组列表一起传递给 executemany我>。如果您需要更多说明,请随时ask a new question
    【解决方案2】:

    打开自动提交 (= True) 以执行 ALTER DATABASE 语句。 "CREATE ... statement not allowed within multi-statement transaction" when using pyodbc

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-07-24
      • 1970-01-01
      • 2020-05-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多