通过python恢复SQL Server备份已启动但未完成
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了通过python恢复SQL Server备份已启动但未完成相关的知识,希望对你有一定的参考价值。
我正在尝试使用python恢复SQL Server备份。我在Tkinter中建立了一个UI,它接受文件路径和其他详细信息,例如服务器名,数据库名等。为进行测试,我仅使用localhost。我已经开发了两个文件。一个名为tool.py的用户界面,而另一个名为Restorebackup.py的文件,该文件具有还原数据库的逻辑。当您运行tool.py文件并按开始按钮时,它会询问您要还原的.dat_bak或.bak文件的文件路径。选择.dat_bak文件后,您需要等待一段时间,并且会收到“还原备份”的消息。我转到SQL Server Management Studio,并查看localhost中的数据库。它显示备份正在用蓝色箭头还原。不幸的是,它永远不会完成,并永远停留在那里。当我通过对文件路径和其他值进行硬编码来运行Restorebackup.py时,它可以完美地还原备份。不确定是什么问题
Restorebackup.py文件
import pyodbc
import os
#######################################################################################################################
def restore_backup(selectedChoiceServer, selectedChoiceDatabase, selectedChoiceSchema, filePath):
try:
driver= '{SQL Server}'
selectedChoiceServer="localhost"
db_environment= 'master'
username= 'sqlserverloginforlocalhost'
password='yourpassword'
connectionString = (('DRIVER='+driver+';PORT=1433;SERVER='+selectedChoiceServer+';PORT=1443;DATABASE='+ db_environment +';UID='+username+';PWD='+ password))
db_connection = pyodbc.connect(connectionString, autocommit=True)
cursor = db_connection.cursor()
cursor.execute("""RESTORE FILELISTONLY FROM DISK = '"""+filePath+"""'""")
dataset = cursor.fetchall()
YourMDFLogicalName = dataset[0][0]
YourLDFLogicalName = dataset[1][0]
if not os.path.exists('c:/mybackup/backuptest'):
os.mkdir('c:/mybackup/backuptest')
db_connection = pyodbc.connect(connectionString, autocommit=True)
sql = """
USE [master]
RESTORE DATABASE [testdatabase] FROM
DISK = N'"""+filePath+"""'
WITH FILE = 1
,RECOVERY
, MOVE N'Service' TO N'C:/mybackup/backuptest/""" + YourMDFLogicalName + """.mdf'
, MOVE N'Service_log' TO N'C:/mybackup/backuptest/""" + YourLDFLogicalName + """.ldf'
, NOUNLOAD, STATS = 5
"""
cursor.execute(sql)
db_connection.autocommit = False
return 1
except Exception as e:
print('Some Error Occured, so backup was not restored')
print('Error is :' + str(e))
return str(e)
#######################################################################################################################
tool.py文件
from tkinter import messagebox
from tkinter import filedialog
import unknown_support
from Restorebackup import restore_backup
import tkinter as Tk
from tkinter import *
def vp_start_gui():
'''Starting point when module is the main routine.'''
global val, w, root
root = Tk()
top = Tool (root)
unknown_support.init(root, top)
root.mainloop()
def destroy_Tool():
root.destroy()
class Tool:
def StartButtonAction(self):
self.Button1.config(state=DISABLED)
selectedChoiceServer = self.Text3.get('1.0','end-1c')
selectedChoiceDatabase = self.Text1.get('1.0','end-1c')
selectedChoiceSchema = self.Text2.get('1.0','end-1c')
root.fileName = filedialog.askopenfilename( filetypes = ( ("bak files", "*.dat_bak"),("All files","*.*") ) )
#root.fileName = root.fileName.replace('/', '\')
res = restore_backup(selectedChoiceServer, selectedChoiceDatabase, selectedChoiceSchema, root.fileName)
if res == 1:
messagebox.showinfo("Backup is restored", "Backup is restored")
else:
messagebox.showinfo("Error: ",res)
def __init__(self, top=None):
'''This class configures and populates the toplevel window.
top is the toplevel containing window.'''
top.geometry("600x450+761+233")
top.title("Backup Restore Tool")
top.configure(background="#d9d9d9")
top.configure(highlightbackground="#d9d9d9")
top.configure(highlightcolor="black")
self.choice = IntVar()
self.databaseChoice = StringVar()
self.Label1 = Label(top)
self.Label1.place(relx=0.32, rely=0.38, height=26, width=58)
self.Label1.configure(background="#d9d9d9")
self.Label1.configure(disabledforeground="#a3a3a3")
self.Label1.configure(foreground="#000000")
self.Label1.configure(text='''Schema''')
self.Label2 = Label(top)
self.Label2.place(relx=0.3, rely=0.24, height=26, width=69)
self.Label2.configure(background="#d9d9d9")
self.Label2.configure(disabledforeground="#a3a3a3")
self.Label2.configure(foreground="#000000")
self.Label2.configure(text='''Database''')
self.Label3 = Label(top)
self.Label3.place(relx=0.3, rely=0.10, height=26, width=69)
self.Label3.configure(background="#d9d9d9")
self.Label3.configure(disabledforeground="#a3a3a3")
self.Label3.configure(foreground="#000000")
self.Label3.configure(text='''Server''')
self.Text1 = Text(top)
self.Text1.place(relx=0.45, rely=0.24, relheight=0.05, relwidth=0.39)
self.Text1.configure(background="#ffffffffffff")
self.Text1.configure(font="TkTextFont")
self.Text1.configure(foreground="black")
self.Text1.configure(highlightbackground="#d9d9d9")
self.Text1.configure(highlightcolor="black")
self.Text1.configure(insertbackground="black")
self.Text1.configure(selectbackground="#c4c4c4")
self.Text1.configure(selectforeground="black")
self.Text1.configure(width=234)
self.Text1.configure(wrap=WORD)
self.Text2 = Text(top)
self.Text2.place(relx=0.45, rely=0.38, relheight=0.05, relwidth=0.39)
self.Text2.configure(background="white")
self.Text2.configure(font="TkTextFont")
self.Text2.configure(foreground="black")
self.Text2.configure(highlightbackground="#d9d9d9")
self.Text2.configure(highlightcolor="black")
self.Text2.configure(insertbackground="black")
self.Text2.configure(selectbackground="#c4c4c4")
self.Text2.configure(selectforeground="black")
self.Text2.configure(width=234)
self.Text2.configure(wrap=WORD)
self.Text3 = Text(top)
self.Text3.place(relx=0.45, rely=0.10, relheight=0.05, relwidth=0.39)
self.Text3.configure(background="white")
self.Text3.configure(font="TkTextFont")
self.Text3.configure(foreground="black")
self.Text3.configure(highlightbackground="#d9d9d9")
self.Text3.configure(highlightcolor="black")
self.Text3.configure(insertbackground="black")
self.Text3.configure(selectbackground="#c4c4c4")
self.Text3.configure(selectforeground="black")
self.Text3.configure(width=234)
self.Text3.configure(wrap=WORD)
self.Button1 = Button(top)
self.Button1.place(relx=0.4, rely=0.58, height=33, width=186)
self.Button1.configure(activebackground="#d9d9d9")
self.Button1.configure(activeforeground="#000000")
self.Button1.configure(background="#d9d9d9")
self.Button1.configure(disabledforeground="#a3a3a3")
self.Button1.configure(foreground="#000000")
self.Button1.configure(highlightbackground="#d9d9d9")
self.Button1.configure(highlightcolor="black")
self.Button1.configure(pady="0")
self.Button1.configure(text='''Start''')
self.Button1.configure(width=186)
self.Button1.configure(command = self.StartButtonAction)
self.Button2 = Button(top)
self.Button2.place(relx=0.4, rely=0.68, height=33, width=186)
self.Button2.configure(activebackground="#d9d9d9")
self.Button2.configure(activeforeground="#000000")
self.Button2.configure(background="#d9d9d9")
self.Button2.configure(disabledforeground="#a3a3a3")
self.Button2.configure(foreground="#000000")
self.Button2.configure(highlightbackground="#d9d9d9")
self.Button2.configure(highlightcolor="black")
self.Button2.configure(pady="0")
self.Button2.configure(text='''Quit''')
self.Button2.configure(width=186)
self.Button2.configure(command = destroy_Tool)
if __name__ == '__main__':
vp_start_gui()
答案
restoreddatabase = selectedChoiceSchema + '-' + strftime("%Y%m%d%H%M%S", gmtime()) +'import'
connectionString = (('DRIVER='+driver+';PORT=1433;SERVER='+localserver+';PORT=1443;DATABASE='+ db_environment +';UID='+localusername+';PWD='+ password))
db_connection = pyodbc.connect(connectionString, autocommit = True)
cursor = db_connection.cursor()
cursor.execute("""RESTORE FILELISTONLY FROM DISK = '"""+filePath+"""'""")
dataset = cursor.fetchall()
filenames = []
for i in dataset:
filedetails = {}
#print(i[0])
#print(i[1][::-1][:(i[1][::-1]).find('\')][::-1])
filedetails['name'] = i[0]
filedetails['withextension'] = i[1][::-1][:(i[1][::-1]).find('\')][::-1]
filenames.append(filedetails)
if not os.path.exists(rootfolder + '/'+ restoreddatabase):
os.mkdir(rootfolder + '/'+ restoreddatabase)
print(filePath)
filePath = filePath.replace("/","\")
rootfolder = rootfolder.replace("/","\")
sql = """
USE [master]
RESTORE DATABASE ["""+restoreddatabase+"""] FROM
DISK = N'"""+filePath+"""'
WITH FILE = 1
,RECOVERY
"""
for i in filenames:
sql = sql + """, MOVE N'""" +i['name'] +"""' TO N'"""+ rootfolder +"""\"""+ restoreddatabase+"""\""" + i['withextension'] + """'"""
sql = sql + """, NOUNLOAD, STATS = 5
"""
cursor.execute(sql)
db_connection1 = pyodbc.connect(connectionString)
cursor1 = db_connection1.cursor()
database_restored_sql = """select top 1 * from msdb.dbo.restorehistory order by restore_date desc"""
cursor1.execute(database_restored_sql)
dataset = cursor1.fetchall()
tquery = """
SELECT session_id as SPID, command, a.text AS Query, start_time, percent_complete, dateadd(second,estimated_completion_time/1000, getdate()) as estimated_completion_time FROM sys.dm_exec_requests r CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) a WHERE r.command in ('BACKUP DATABASE','RESTORE DATABASE')
"""
print('Percentage Restored: ', end = '', flush = True)
#sys.stdout.write('Percentage Restored: ')
while restoreddatabase != dataset[0][2]:
#print('in while')
sleep(5)
cursor1.execute(database_restored_sql)
dataset = cursor1.fetchall()
cursor1.execute(tquery)
tdataset = cursor1.fetchall()
if len(tdataset) > 0:
if tdataset[0][4] > 99.99:
print(str(tdataset[0][4])[0:3] + '%',end = ' ', flush = True)
#sys.stdout.write(str(tdataset[0][4])[0:3] + '% ')
else:
print(str(tdataset[0][4])[0:2] + '% ',end = ' ', flush = True)
#sys.stdout.write(str(tdataset[0][4])[0:2] + '% ')
db_connection.autocommit = False
以上是关于通过python恢复SQL Server备份已启动但未完成的主要内容,如果未能解决你的问题,请参考以下文章
《SQL Server企业级平台管理实践》读书笔记——几个系统库的备份与恢复