孤荷凌寒自学python第四十七天通用跨数据库同一数据库中复制数据表函数
(完整学习过程屏幕记录视频地址在文末)
【所有学习笔记及源代码已由热心网友汇总打包(也有学习过程4k高清屏录)(但因打包工作量大,因此并非完全免费)此处 此处】4K录屏过程全部85天。
今天继续建构自感觉用起来顺手些的自定义模块和类的代码。
今天打算完成的是通用的(至少目前操作四种数据库)在同一数据库内复制数据表的方法函数。
此设想最初我自我感觉都非常简单,然而事实是关系型数据库统一使用的sql语言在各家其实是完全不同的!
一、了解清楚了我目前研究的四种关系型数据库对同一数据库中数据表的复制操作的sql语句的异同也效果局限
(一)Access 数据库与mssql数据库
access数据库一般是作为轻量级的桌面级关系型数据库使用的;
而mssql是微软出品的大型商用服务器型关系数据库。
它们的复制数据表的操作sql语句一样如下:
1.只复制数据表的结构而不复制源表的数据
select *
into [目标表] from [源表] where 1=2;
最后的【where 1=2】就是限制连同数据一起复制的条件式,只要条件式返回的布尔值为false即可。
2.要复制数据表结构与所有数据
select *
into [目标表] from [源表];
【注意】:通过这样的方式复制的数据表,目标表中虽然完整的复制了源表的所有列的类型、大小等信息,却唯独不会包含各列是否是【主键】的信息。
这其实也就是说复制已经基本失败了。
(二)sqlite数据库
sqlite是本地化的轻量级关系型数据库。
它的复制代码如下
1.只复制数据表的结构而不复制源表的数据
CREATE
TABLE [目标表] AS SELECT * FROM [源表] WHERE 1=2;
最后的【where 1=2】就是限制连同数据一起复制的条件式,只要条件式返回的布尔值为false即可。
2.要复制数据表结构与所有数据
CREATE
TABLE [目标表] AS SELECT * FROM [源表];
【注意】:通过这样的方式复制的数据表,目标表中虽然完整的复制了源表的所有列的类型、大小等信息,却唯独不会包含各列是否是【主键】的信息。
这其实也就是说复制已经基本失败了。
(三)mysql数据库
mysql是中大型服务器型关系数据库。
它的复制数据表的sql语句如下:
1.只复制数据表的结构而不复制源表的数据
CREATE
TABLE [目标表] LIKE [源表];
2.要复制数据表结构与所有数据
将在只复制数据表结构的基础上再多执行一步:
CREATE
TABLE [目标表] LIKE [源表];
INSERT
INTO [目标表] SELECT * FROM [源表];
【注意】在目前我正在研究的四种数据库中,只有mysql的表复制sql语句是完美无瑕的。
但:
CREATE
TABLE [目标表] LIKE [源表];
只支持mysql5.0以上的版本,多数资料是这样说的,没有亲自验证。
二、尝试解决另外三种数据库的复制数据表后不包括主键列信息的问题
因为复制代码执行后,目标数据表并没有主键列的信息,那就只有后面手动添加上了。
(一)Access
并不能直接修改一个列为主键列。
于是根据所查阅到的资料,整理笔记如下:
1.先在数据表中删除要作为主键的列:
alter
table [目标表] drop COLUMN [要作为主键的列名];
2.再添加这个列,并在添加列的sql代码中指定此列为主键。
alter
table [目标表] add COLUMN [要作为主键的列名] 列的数据类型 NOT
NULL PRIMARY KEY;
【注意】access只能通过sql语句添加一个列作为主键。虽然在access软件中可以手动指定多个列作为主键组。
(二)mssql
mssql可以直接修改一个列或多个列为主键列
'alter
table [目标表] add CONSTRAINT
PK_目标表名称 PRIMARY KEY
NONCLUSTERED (一个或多个要作为主键的列名称);'
当然之前复制数据表时是连同表中数据一起复制的,那么必须保证要作为主键的列中各行中是没有null值的数据的。
如有必要,应当将此列先指定为not null型的列,sql语句如下:
alter
table [目标表] alter column [一个要作为主键的列名] 列的数据类型 not null
【注意】虽然mssql可以指定多个列作为主键,然而如果这么做了,若只是复制数据表的结构倒没有问题,然而一旦要连同数据也一起复制,多数情况下会报完全不相关莫名其妙的错误。
(三)sqlite
sqlite的sql语句根本就不能对一个表的中的列的任何属性进行修改,也不能删除列。
因此常规方法都已失效。
好在sqlite中有一个相对而言比较简单的获取一个数据表所有列完整信息结构的sql语句:
PRAGMA
table_info([源表名]);
通过此sql代码将得到一个嵌套列表对象,
通过相关列表转换操作,就可以还原数据源表的创建表的sql语句,然后重新创建一个新表,也算作是完成了数据表的结构复制。
如果再要复制数据,则执行 insert into 语句即可从源表向目标表中导入数据
这算是变通地完成了数据表的复制。
三、今天竭尽全力部分完成了通用的同一数据库中数据表的复制操作的函数
修改后的整个_mdb.py文件如下:
#!/usr/bin/env
python3
# -*-
coding: utf-8 -*-
importpypyodbc
importpymysql
importpymssql
importsqlite3
importcx_Oracle
importos.path
import_mty
mdbErrString='' #供其它模块使用的全局变量了,实时保存了各函数执行时的错误信息
#下面是失败的连接oracle数据库的测试,已放弃,提示的错误是服务器那边认为用户名密码错误
def connectoracle():
con=cx_Oracle.connect("sys/123
as [email protected]/orcl")
#con=cx_Oracle.connect("adminstrator/
as [email protected]/orcl")
cur=con.cursor()
cur.execute('select * from m;')
data=cur.fetchall()
print(list(data))
con.close()
def msgbox(info,titletext='孤荷凌寒的DB模块对话框QQ578652607',style=0,isShowErrMsg=False):
return_mty.msgboxGhlh(info,titletext,style,isShowErrMsg)
#连接网络数据库,目前支持mssql,mysql
def conNetdbGhlh(serveraddress,usr,pw,dbname,dbtype='mssql',isShowMsg=False):
'''
用于连接网络数据库,目前支持连接mssql,mysql两种网络关系型数据库,
dbtype可选形参默认值是操作mssql,如果要连接Mysql则通过此可选形参指定:为mysql
,此函数返回一个connect数据库连接对象
'''
globalmdbErrString
mdbErrString=''
try:
ifdbtype=='mssql':
con=pymssql.connect(serveraddress,usr,pw,dbname,charset='utf8')
returncon
elifdbtype=='mysql':
con=pymysql.connect(serveraddress,usr,pw,dbname)
returncon
else:
return None
except Exception ase:
mdbErrString='连接网络数据库【' + serveraddress + '】【' + dbname + '】时出错:' + str(e) + '\n此函数由【孤荷凌寒】创建,QQ578652607'
ifisShowMsg==True:
msgbox(mdbErrString)
return None
else:
pass
finally:
pass
#连接本地数据库文件,目前支持db,mdb,accdb,s3db
def conLocaldbGhlh(dbfilepath,strPass='',isShowMsg=False):
'''
连接本地数据库文件,目前支持mdb,accdb,以及sqlite数据库文件,识别方法是,如果有后缀mdb或accdb,则说明是access数据库文件,否则都认为是sqlite数据库文件。
如果连接成功,将返回一个con数据库连接对象
'''
globalmdbErrString
mdbErrString=''
try:
strhznm=_mty.getFilehzGhlh(dbfilepath)
ifstrhznm.find('mdb')>-1 orstrhznm.find('accdb')>-1:
#---连接access数据库----
ifstrPass=='':
strname='Driver={Microsoft
Access Driver (*.mdb, *.accdb)};DBQ='+ dbfilepath
else:
strname='Driver={Microsoft
Access Driver (*.mdb, *.accdb)};DBQ=' + dbfilepath + ';Pwd='+strPass
con=pypyodbc.connect(strname)
returncon
else:
#----连接sqlite数据库-----
con=sqlite3.connect(dbfilepath)
returncon
except Exception ase:
mdbErrString='连接网络数据库文件【' + dbfilepath + '】时出错:' + str(e) + '\n此函数由【孤荷凌寒】创建qq号是578652607'
ifisShowMsg==True:
msgbox(mdbErrString)
return None
else:
pass
finally:
pass
#删除数据库中的表
def delTableGhlh(con,strtablenm,isShowMsg=False):
'''
此方法将删除指定conn中的table,不管table中是否有数据,因此 操作要谨慎
,成功返回True 失败返回False
'''
globalmdbErrString
mdbErrString=''
try:
ifstrtablenm is not None andstrtablenm != '':
sql ='DROP TABLE '+strtablenm
cur=con.cursor()
cur.execute(sql)
con.commit()
cur.close()
ifisShowMsg==True:
msgbox('删除数据库表[{}]成功!'.format(strtablenm))
return True
else:
ifisShowMsg==True:
msgbox('the [{}] is
empty or equal None!'.format(sql))
return False
except Exception ase:
mdbErrString='删除数据库中表【' + strtablenm + '】时出错:' + str(e) + '\n此函数由【孤荷凌寒】创建qq号是578652607'
ifisShowMsg==True:
msgbox(mdbErrString)
return False
else:
pass
finally:
pass
#创建一个新表
def newTableGhlh(con,strTableNm,lstnm:'list. 将所有要新建的字段都放在一个列表中',lsttype,lstlength,dbtype='acc',lstNull=None,isDelExitsTable = False ,isAutoSetIDfieldAutoNumber=True,strSetFieldAutoNumberName='id',isShowMsg= False):
'''
传递有关表的每个字段的三大属性的分别的三个列表,
并可以指定此表的PRIMARY
key 字段,
及指定是否自动识别ID字段为PRIMARY key 字段,
如果要创建的表名是否存在,约定是否删除旧表
如果设置为不删除旧表,则放弃新建表;
'''
globalmdbErrString
mdbErrString=''
try:
cur=con.cursor()
dbtype=dbtype.lower()
ifdbtype=='access':
dbtype='acc'
except:
pass
#--------------------------------------------------------
try:
ifstrTableNm == "" or strTableNm.lower()
== "select" or strTableNm.lower()
== "from" or strTableNm.lower()
== "where" or strTableNm.lower()
== "order" or strTableNm.lower()
== "insert" or strTableNm.lower()
== "delete" or strTableNm.lower()
== "in" or strTableNm.lower()
== "with" or strTableNm.find("[") >-1 orstrTableNm.find("]") >-1:
mdbErrString ="要创建的数据表名为空或为不合法的保留关键字,请重新确认数据表名。" + '\n此函数由【孤荷凌寒】创建qq是578652607'
ifisShowMsg == True:
msgbox(mdbErrString)
return False
if len(lstnm)
!= len(lsttype) or len(lsttype)
!= len(lstlength):
mdbErrString ="在新建一个数据表时,接收到的四个关于表中字段属性的列表参数中元素总数不相同,无法执行。" + '\n此函数由【孤荷凌寒】创建qq号:578652607'
ifisShowMsg == True:
msgbox(mdbErrString)
return False
#现在先检查表是否存在,如果存在,根据设置是删除旧表然后新建表呢,还是保留旧表而不新建表
ifisTableExistGhlh(con,strTableNm,isShowMsg)==True:
#--如果旧表存在,就看是否要删除旧表---
ifisDelExitsTable==True:
ifdelTableGhlh(con,strTableNm,isShowMsg)==False:
#--旧表存在,但是却删除失败的情况----
mdbErrString ="在新建一个数据表时,因为同名的旧表已经存在了,但尝试删除旧表失败,所以无法新增一个表。" + '\n此函数由【孤荷凌寒】创建qq:578652607'
ifisShowMsg == True:
msgbox(mdbErrString)
return False
else:
#成功删除了旧表,那么就添加新表,直接顺序到后面执行代码即可。
pass
else:
#如果旧表存在,但又指定不删除旧表,那么只好结束 本函数 过程了
mdbErrString ="在新建一个数据表时,因为同名的旧表已经存在了,而又指定不能删除旧表,所以无法新增一个表。" + '\n此函数由【孤荷凌寒】创建qq是578652607'
ifisShowMsg == True:
msgbox(mdbErrString)
return False
#现在准备开始添加新的表-----
intC=len(lstnm)
rals=range(intC)
strR=""
strRls=""
strNm=""
strLs=""
intL=0
strL=""
strN=""
for i inrals:
strNm=lstnm[i]
strLs =lsttype[i]
strLs =getStandardFieldTypeGhlh(strLs,dbtype,isShowMsg)
strLs=' '+ strLs
#-----------------------
intL=lstlength[i]
ifintL<=0:
strL=''
else:
strL="(" + str(intL) +")"
#----------------
strN=""
if lstNull
!= None:
try:
strN=lstNull[i]
except:
pass
#---------------
if strN=="" or strN==None:
strN=""
else:
strN=" "+ strN
#----------
ifstrLs.find('NULL')>=0:
#-----如果已经在得到类别时,已经在字符串中出现了null关键字,此处就不要再处理了
strN=""
#---------------
ifdbtype!='mysql':
#上一条件式是因为,Mysql不允许在sql语句中出现 []括号
strNm='[' + strNm
+ ']'
strRls=strNm + strLs + strL + strN# 此时已经构建了类似于 【name
varchar(20)】 这样的内容了
#检查是否主键--
ifisAutoSetIDfieldAutoNumber==True:
#如果强制将字段名称为“id”的字段作为主键,则
ifstrNm.lower()==strSetFieldAutoNumberName.lower():
ifstrR.find("PRIMARY KEY")<0:
#上一条件式是为了避免有多个primary key
ifstrRls.find("PRIMARY KEY")<0:
#上一条件式是为了防止在取得可用字段类型时已添加过Primary key 了
strRls=strRls+"
PRIMARY KEY"
#现在拼合 strR
if strR=="":
strR=strRls
else:
strR=strR +","+strRls
#开始生成sql语句
strSql='CREATE TABLE ' +
strTableNm + '(' + strR + ');'
#运行--
cur.execute(strSql)
con.commit()#提交所作的修改
#如果没有出错,就返回成功
return True
[if !supportLineBreakNewLine]
[endif]
except Exception ase:
mdbErrString='尝试创建表【' + strTableNm + '】时出错:' + str(e) + '\n此函数由【孤荷凌寒】创建qq号是578652607'
ifisShowMsg==True:
msgbox(mdbErrString)
return False
else:
pass
finally:
try:
cur.close()
except:
pass
#判断一个表在数据库中是否存在
def isTableExistGhlh(con,strtablenm,isShowMsg=False):
'''
判断一张表是否在数据库中存在
,需要传入con数据库连接对象
'''
globalmdbErrString
mdbErrString=''
try:
cura=con.cursor()
returnisTableExist2Ghlh(cura,strtablenm,isShowMsg)
except Exception ase:
mdbErrString='检查表【' + strtablenm + '】是否存在时出错(此错误一般说明表不存在):' + str(e) + '\n此函数由【孤荷凌寒】创建qq号是578652607'
ifisShowMsg==True:
msgbox(mdbErrString)
return False
else:
pass
finally:
try:
cura.close
#pass
except:
pass
#判断一个表在数据库中是否存在2
def isTableExist2Ghlh(cur,strtablenm,isShowMsg=False):
'''
判断一张表是否在数据库中存在
,需要传入数据库操作指针对象
'''
globalmdbErrString
mdbErrString=''
try:
strsql='SELECT * FROM ' +
strtablenm + ';'
cur.execute(strsql)
return True
except Exception ase:
mdbErrString='检查表【' + strtablenm + '】是否存在时出错(此错误一般说明表不存在):' + str(e) + '\n此函数由【孤荷凌寒】创建qq号是578652607'
ifisShowMsg==True:
msgbox(mdbErrString)
return False
else:
pass
finally:
pass
#将各种复杂的对数据库类型的描述,如3,8等数值表示的字段类型与,windows系统中的system.string,之类的描述,统一修改为数据库能够在定义字段类型时直接使用的描述字符串
def getStandardFieldTypeGhlh(strin,dbtype='acc',isShowMsg=False):
'''
将各种复杂的对数据库类型的描述,如3,8等数值表示的字段类型与,windows系统中的system.string,之类的描述,统一修改为数据库能够在定义字段类型时直接使用的描述字符串
'''
globalmdbErrString
mdbErrString=''
strI=""
try:
strI=str(strin)
strI.lower()
strI=strI.replace('system.','') #windows系统中,以及其它一些语言中对数据类型的描述的字符串中,可以包含有system.前缀
strI=strI.replace('.','') #去掉多余的点
dbtype=dbtype.lower()
ifdbtype=='access':
dbtype='acc'
except:
pass
#--------------------------------------------------------
try:
if strI=='':
mdbErrString ="因为传入的要识别的数据库的字段类型为空,因此无法识别,只能识别成【文本类型】【text】。" + '\n此函数由【孤荷凌寒】创建qq:578652607'
ifisShowMsg == True:
msgbox(mdbErrString)
ifdbtype!='acc' and dbtype!='mysql':
return 'ntext'
else:
return 'text'
#---正式识别开始---------------------
if strI in ("int32", "3", "int","int16", "integer", "long","smallint","tinyint","mediumint"):
ifdbtype=='acc':
return 'long'
else:
return "int" #多数数据库在这种情况下要额外指定长度
#----------------------
if strI=='bigint':
ifdbtype=='acc' or dbtype=='sqlite':
return 'int'
else:
return 'bigint'
#-----------------
elif strI in ("memo","longtext","mediumtext"):
ifdbtype=='acc':
return "memo"
elifdbtype=='mysql':
return "longtext"
else:
return 'ntext'
#------------------
elif strI in ("str","string","8","varchar","char","text","nvarchar","tinytext"):
if dbtype=='mysql' ordbtype=='acc':
return "varchar" #在这种情况下都需要指定长度
else:
return "nvarchar" #在这种情况下都需要指定长度
#------------------
elif strI in ("datetime","7"):
ifdbtype=='sqlite':
return "date"
else:
return "datetime"
#----------------
elif strI=="date":
ifdbtype!='acc':
return "date"
else:
return "datetime"
#-----------------
elif strI=="time":
ifdbtype!='acc':
return "time"
else:
return "datetime"
#-----------------
elif strI in ("single", "4", "real"):
return "real"
#----------------
elif strI in ("double", "5", "float"):
return "float"
#----------------
elif strI in ("boolean", "11", "bit","bool"):
ifdbtype=='mssql' or dbtype=='acc':
return "bit"
else:
return 'boolean'
#-----------------
elif strI in ("byte[]", "8209", "image", "binary", "ole"):
#---image为微软专用的OLE,"Binary"为 二进制,在sqlite中使用blob,表示二进制大数据
ifdbtype=='acc' or dbtype=='mssql':
return "Image"
elifdbtype=='sqlite':
return 'blob'
else:
return 'binary'
#-------这是真正的全精度数据
elif strI in ("decimal", "14", "money","numeric"):
ifdbtype=='sqlite':
return 'numeric'
elifdbtype=='acc':
return 'money'
else:
return 'decimal'
#--------------
elif strI=="timestamp":
ifdbtype=='acc':
return 'double'
else:
return 'timestamp'
#------自动编号------
elif strI in ("auto", "autocount", "autonumber", "autono", "autoincrement","auto_increment"):
ifdbtype=='mysql':
return 'int NOT
NULL auto_increment'
elifdbtype=='acc':
return 'counter
NOT NULL PRIMARY KEY'
elifdbtype=='mssql':
return 'int
identity(1,1)'
else:
#--sqlite-----------------
return "integer
PRIMARY KEY AUTOINCREMENT NOT NULL"
#--------
else:
#其余情况,全部识别为 text
ifdbtype!='acc' and dbtype!='mysql':
return 'ntext'
else:
return 'text'
except Exception ase:
mdbErrString='尝试将各种不同的对数据库字段类型的描述转换为标准字段类型描述时出错:' + str(e) + '\n此函数由【孤荷凌寒】创建qq号是578652607'
ifisShowMsg==True:
msgbox(mdbErrString)
#------------------------------------------
ifdbtype!='acc' and dbtype!='mysql':
return 'ntext'
else:
return 'text'
else:
pass
finally:
pass
(内容太长,被分成两部分了)
(见第二部分)