python实现对SQL server数据库备份自动还原操作及表和字段提取方法、踩坑

    实现还原操作思路是通过SQL server命令执行还原语句,分为:1.数据库连接;2.sql语句的执行;3.sql执行完成的校检;4.关闭连接;

    IDE:pycharm

    JDK:python3.8

     包:pyodbc4.0.30

代码呈上:

import pyodbc
import time
import decimal
import re

# 数据库创建路径
datebasePath = 'D:\\pythonspace\\datebasePath\\'
#数据库ip
server = 'localhost,1433'
#端口号
#port = '1433'
#用户名
uid = 'sa'
#密码
pwd = 'sa123'
#数据库名称,即要访问的数据库
db = 'master'
#通过pyodbc要访问的数据库连接的驱动
driver = 'SQL server'

class SQLSERVER:
    def __init__(self, server, port, uid, pwd, db, driver):
        #基本构造函数
        self.server = server
        self.uid = uid
        self.port = port
        self.pwd = pwd
        self.db = db
        self.driver = driver

    #数据库连接方法,调用方法后即可使用self.conn和方法返回的cur游标
    def GetConnect(self):
        #校检
        if not self.db:
            raise (NameError, '没有设置数据库信息')
        # self.conn = pyodbc.connect(SERVER=self.server, port=self.port, UID=self.uid, PWD=self.pwd, DATABASE=self.db,
        #                            DRIVER=self.DRIVER, autocommit=True)
        #打印数据库驱动
        #示例:
        #['SQL Server', 'SQL Server Native Client 10.0']
        print(pyodbc.drivers())
        #数据库连接操作,注意事项:server这里是ip地址包括端口号;这里autocommit要为true不然操作数据库命令和写表操作只临时执行没有提交到事务内执行
        self.conn = pyodbc.connect(SERVER=self.server, UID=self.uid, PWD=self.pwd, DATABASE=self.db,
                                    driver=self.driver, autocommit=True)
        # self.conn.autocommit(True)
        #new出游标为执行sql语句提供执行操作
        cur = self.conn.cursor()
        if not cur:
            raise (NameError, '连接数据库失败')
        else:
            return cur

    # sql执行读操作
    def ExecQuery(self, sql):
        cur = self.GetConnect()
        cur.execute(sql)
        resList = cur.fetchall()
        cur.close()
        self.conn.close()
        return resList

    # sql执行写操作
    def ExecNonQuery(self, sql):
        cur = self.GetConnect()
        cur.execute(sql)
        self.conn.commit()
        cur.close()
        self.conn.close()

    # 还原备份数据到数据库
    def restoreDb(self, database, restorepath):
        #获取数据库连接参数的方法
        cur = self.GetConnect()
        # 通过RESTORE FILELISTONLY查询备份文件原始还原路径,这里意在找到备份时存的名称
        selectSql = "RESTORE FILELISTONLY FROM DISK = N'{0}'".format(restorepath)
        #执行sql语句
        cur.execute(selectSql)
        #cur.fetchall是获取返回的结果集
        relist = cur.fetchall()
        #结果示例
        #[('w', 'D:\\sqlserverData\\MSSQL10_50.MSSQLSERVER\\MSSQL\\DATA\\w.mdf', 'D', 'PRIMARY', 3145728, 35184372080640, 1, Decimal('0'), Decimal('0'), 'BD58B9C4-4A71-4EBF-9F61-6E651F129ACF', Decimal('0'), Decimal('0'), 1507328, 512, 1, None, Decimal('0'), '00000000-0000-0000-0000-000000000000', False, True, None),\
        # ('w_log', 'D:\\sqlserverData\\MSSQL10_50.MSSQLSERVER\\MSSQL\\DATA\\w_log.ldf', 'L', None, 1048576, 2199023255552, 2, Decimal('0'), Decimal('0'), '7DB0FCEF-BEFB-4D5B-90CF-CDC8BB2FB007', Decimal('0'), Decimal('0'), 0, 512, 0, None, Decimal('0'), '00000000-0000-0000-0000-000000000000', False, True, None)]
        print(relist)
        #这一步是拼接执行还原语句时需要还原的备份文件路径及还原的日志文件路径
        mdfName = relist[0][0]
        mdfValue = relist[0][1]
        mdfValueList = mdfValue.split('\\')
        mdfValueStr = datebasePath + mdfValueList[len(mdfValueList)-1]
        ldfName = relist[1][0]
        ldfValue = relist[1][1]
        ldfValueList = ldfValue.split('\\')
        ldfValueStr = datebasePath + ldfValueList[len(mdfValueList) - 1]
        #这一步是SQL server还原语句拼接
        sql = "use master RESTORE DATABASE [{0}] FROM DISK = N'{1}' WITH replace,RECOVERY," \
              "MOVE '{2}' TO '{3}',MOVE '{4}' TO '{5}'".format(database, restorepath, mdfName, mdfValueStr, ldfName,
                                                               ldfValueStr)
        cur.execute(sql)
        print(sql)
        #这一步很重要是还原语句执行后等待有返回结果操作,如果没有这一步,还原的数据库会一直显示状态为正在还原\
        # (题外话:当时就是没有写这一步导致浪费了我一上午时间在测试这个一直显示数据库状态为正在还原中)
        while cur.nextset():
            pass
        print("还原执行完成")
        #返回还原的数据库名称
        return database

    # 还原前操作,为备份还原的数据库命名
    def reductionDB(self, path):
        #提取路径下的文件名
        dbnameList = path.split('\\')
        dbname = dbnameList[len(dbnameList) - 1].split('.')[0]
        # cratedb=self.createDB(dbname)
        #去除名称中有的特殊符号(注:特殊符号在删除数据库语句中会出现报错)
        dbnameStr = re.sub(u'([^\u4e00-\u9fa5\u0030-\u0039\u0041-\u005a\u0061-\u007a])', "", dbname)
        #还原方法
        msg = self.restoreDb(dbname, path)
        # 返回数据库名称
        return msg

    # 查询还原的数据库内所有表及字段
    def selectTable(self, datebase):
        print("查询表字段开始")
        conn = pyodbc.connect(SERVER=self.server, port=self.port, UID=self.uid, PWD=self.pwd, DATABASE=datebase,
                              driver=self.driver, autocommit=True)
        cur = conn.cursor()
        #查询当前连接的数据库内表名称及字段名称的sql语句
        sqlList = '''SELECT  d.name AS 表名 ,
                a.name AS 字段名
                FROM    syscolumns a
                LEFT JOIN systypes b ON a.xtype = b.xusertype
                INNER JOIN sysobjects d ON a.id = d.id
                                           AND d.xtype = 'U'
                                           AND d.name <> 'dtproperties'
                LEFT JOIN syscomments e ON a.cdefault = e.id
                LEFT JOIN sys.extended_properties g ON a.id = g.major_id
                                                       AND a.colid = g.minor_id
        ORDER BY a.id ,
                a.colorder'''
        cur.execute(sqlList)
        resList = cur.fetchall()
        #注意:这里是新建的一个连接需要关闭
        cur.close()
        conn.close()
        print(resList)
        #返回结果示例:
        #[('table_zoo', 'id'), ('table_zoo', 'name'), ('table_zoo', 'cat'), ('table_zoo', 'dog'), ('table_info', 'id'), ('table_info', 'name'), ('table_info', 'idcard')]
        print("查询表字段结束")


        return resList

    # 创建数据库
    def createDB(self, database):
        self.conn = pyodbc.connect(SERVER=self.server, UID=self.uid, PWD=self.pwd, driver=self.driver, autocommit=True)
        cur = self.conn.cursor()
        sql = '''use master
                 CREATE DATABASE {0}
                 on PRIMARY
                (
                    name = {0},
                    FILENAME = '{1}{0}.mdf'
                )
                log ON
                (
                    name = '{0}_log',
                    FILENAME = '{1}{0}_log.ldf'
                )
               '''.format(database,datebasePath)
        cur.execute(sql)
        cur.close()
        self.conn.close()
        return database

    #删除数据库
    def deleteDb(self, datebase):
        # 数据库删除
        print("删除数据库开始")
        #这里单独建立连接是因为我要进去到master库来删除其他库
        conn = pyodbc.connect(SERVER=self.server, UID=self.uid, PWD=self.pwd, DATABASE='master',
                              driver=self.driver, autocommit=True)
        cur = conn.cursor()
        #删除数据库时,我们要先将数据库设置为单个用户访问模式,不然的话删不掉,因为有人占用是无法删除的
        sqlOnly='''ALTER DATABASE [{0}] SET  SINGLE_USER WITH ROLLBACK IMMEDIATE'''.format(datebase)
        sql = 'USE master DROP DATABASE {0}'.format(datebase)
        cur.execute(sqlOnly)
        #这里为什么要设置休眠时间,是因为上一个sql语句还未执行完,下一个语句就可能没法删除
        time.sleep(5)
        cur.execute(sql)
        while cur.nextset():
            pass
        cur.close()
        conn.close()
        print("删除数据库结束")

def main():
    ms = SQLSERVER(server='localhost', port='1433', uid='sa', pwd='sa123', db="master", driver='SQL server')
    try:
        datebase = ms.reductionDB('D:\\ma.bak')
        list = ms.selectTable(datebase)
        ms.conn.close()
        ms.deleteDb(datebase)
    except Exception:
        ms.conn.close()
        print(Exception.with_traceback())
        print("异常报错")


if __name__ == '__main__':
    main()

你可能感兴趣的:(python对SQL,server的操作)