【问题标题】:Waiting for DB restore to finish using sqlalchemy on SQL Server 2008在 SQL Server 2008 上使用 sqlalchemy 等待数据库还原完成
【发布时间】:2011-05-14 08:46:11
【问题描述】:

我正在尝试在开发过程中自动化我的数据库恢复,在 SQL Server 2008 上使用 TSQL,使用带有 pyodbc 的 sqlalchemy 作为传输。

我正在执行的命令是:

"""创建数据库数据库名称

restore database dbname FROM DISK='C:\Backups\dbname.bak' WITH REPLACE,MOVE 'dbname_data' TO 'C:\Databases\dbname_data.mdf',MOVE 'dbname_log' TO 'C:\Databases\dbname_log .ldf'"""

不幸的是,在 SQL Management Studio 中,代码运行后,我看到数据库仍处于“正在恢复...”状态。

如果我通过管理工作室恢复,它可以工作。如果我使用子进程调用“sqlcmd”,它可以工作。 pymssql 在身份验证方面存在问题,甚至没有达到那么远。

可能出了什么问题?

【问题讨论】:

  • 我在使用 sqlserver 2012 时遇到了完全相同的问题,即使使用 django 提供连接 cursor。没用:DROPRESTORE 之前,WITH REPLACE, RECOVERY, NOUNLOADSET SINGLE_USERRESTORE 之前和SET MULTI_USER 之后,USE [master]RESTORE 之前,SET SINGLE_USER WITH ROLLBACK IMMEDIATE 之前。就像你一样,这些都在 Management Studio 中运行时工作。

标签: sql-server tsql sqlalchemy restore pyodbc


【解决方案1】:

执行以下操作无法重现直接从 pyodbc(没有 sqlalchemy)恢复的问题:

connection = pyodbc.connect(connection_string) # ensure autocommit is set to `True` in connection string
cursor = connection.cursor()
affected = cursor.execute("""CREATE DATABASE test
RESTORE DATABASE test FROM DISK = 'D:\\test.bak' WITH REPLACE, MOVE 'test_data' TO 'D:\\test_data.mdf', MOVE 'test_log' to 'D:\\test_log.ldf' """)
while cursor.nextset():
    pass

一些需要澄清的问题:

  • 使用 sqlalchemy 进行还原的代码是什么?
  • 正在使用什么版本的 SQL Server ODBC 驱动程序?
  • SQL Server 日志中是否有任何与还原相关的消息?

感谢geographika 提供Cursor.nextset() 示例!

【讨论】:

  • 如果恢复事务日志是问题,那么在 Management Studio 中运行 TSQL 不会同样失败吗?
  • 是的,我想我没有仔细阅读。奇怪的是,确切的 T-SQL 与 pyodbc 一起工作,但当 sqlalchemy 用作包装器时却不行。考虑到问题的年龄,我们可能无法获得问题的答案来缩小问题范围。
  • @hobs 使用 pyodbc 3.0.7 和 SQL Server Native Client 11.0,我现在收到错误:[Microsoft][SQL Server Native Client 11.0][SQL Server]CREATE DATABASE statement not allowed within multi-statement transaction. 如果你真的需要事先创建数据库,你最好分手创建和恢复。否则,只需运行 RESTORE DATABASE 本身 - 如果数据库不存在,将创建它。
  • @hobs 作为后续,我必须添加以初始化光标并调用Cursor.nextset(),如geographika's 回答中所述,以便完成还原。希望这会有所帮助。
  • 修复了我的问题,即我的 bak 文件包含不同数据库的 .mdf 和 .ldf 文件(我的 bak 文件命名错误)。正如您在注释代码中指出的那样,我还使用了connection = pyodbc.connect(connection_string, autocommit=True)。不知道这是否是解决方案的一部分。感谢您的详细帮助。我正在使用 TDS 7.2 版、SQL Server Express 2012、pyodbc 3.0.6。在所有CREATEDROP 命令之后,我做了一个connection.close()。在RESTORE 之后不需要connection.commit()
【解决方案2】:

BACKUP 和 RESTORE 语句异步运行,因此在继续执行其余代码之前它们不会终止。

使用http://ryepup.unwashedmeme.com/blog/2010/08/26/making-sql-server-backups-using-python-and-pyodbc/ 中描述的 while 语句为我解决了这个问题:

# setup your DB connection, cursor, etc
cur.execute('BACKUP DATABASE ? TO DISK=?', 
            ['test', r'd:\temp\test.bak'])
while cur.nextset():
    pass

【讨论】:

    【解决方案3】:

    五件事解决了我的问题,症状相同。

    1. 发现我的 test.bak 文件包含错误的 mdf 和 ldf 文件:

      >>> cursor.execute(r"RESTORE FILELISTONLY FROM DISK = 'test.bak'").fetchall()    
      [(u'WRONGNAME', u'C:\\Program Files\\Microsoft SQL ...),
      (u'WRONGNAME_log', u'C:\\Program Files\\Microsoft SQL ...)]
      
    2. 创建了一个新的 bak 文件并确保设置了 copy-only backup 选项

    3. 为我的连接设置自动提交选项。

      connection = pyodbc.connect(connection_string, autocommit=True)
      
    4. 仅将 connection.cursor 用于单个 RESTORE 命令,仅用于其他命令

    5. 将我的 RESTORE 命令中的 test_data MOVE 更正为 test(由 @beargle 提供)。

      affected = cursor.execute("""RESTORE DATABASE test FROM DISK = 'test.bak' WITH REPLACE, MOVE 'test' TO 'C:\\test.mdf', MOVE 'test_log' to 'C:\\test_log.ldf' """)
      

    【讨论】:

      【解决方案4】:

      对于 SQL Alchemy 用户,感谢 geographika 的回答:我最终使用了连接池中的“原始”DBAPI connection

      它与 geographika 的解决方案完全一样,但有一些额外的部分:

      import sqlalchemy as sa
      driver = 'SQL+Server'
      name = 'servername'
      sql_engine_str = 'mssql+pyodbc://'\
                           + name\
                           + '/'\
                           + 'master'\
                           + '?driver='\
                           + driver
      engine = sa.create_engine(sql_engine_str, connect_args={'autocommit': True})
      
      connection = engine.raw_connection()
      try:
        cursor = connection.cursor()
        sql_cmd = """
              RESTORE DATABASE [test]
              FROM DISK = N'...\\test.bak'
              WITH FILE = 1,
               MOVE N'test'
               TO N'...\\test_Primary.mdf',
               MOVE N'test_log'
               TO N'...\\test_log.ldf',
               RECOVERY,
               NOUNLOAD,
               STATS = 5,
               REPLACE
              """
        cursor.execute(sql_cmd)
        while cursor.nextset():
             pass
      except Exception as e:
        logger.error(str(e), exc_info=True)
      

      【讨论】:

        猜你喜欢
        • 2010-12-17
        • 2023-04-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-10-04
        相关资源
        最近更新 更多