使用Python的pymysql包与Mysql进行交互——连接、建库、建表、插入数据
☆☆文档均为作者手写,大部分源码都已经贴上。
‘赠人玫瑰、手留余香’,如果这篇文档对您有帮助,请给作者点一个小红心,万分感谢。
文章目录
0 背景及配置
0.1 背景
作者接触到的某一项目,由于Python可使用库非常多、处理效率较高等优点。所以,在该项目运用到Python与Mysql进行了如下交互:
- 从爬虫、收集统计文献等渠道获取的数据要存入到项目原始数据库,可以利用Python与XML的交互将EXCEL或其他形式存储的数据转为固定好XML格式的数据,然后通过JAVA后端写好的平台,以XML格式的文件将数据传输至Mysql.
- 从Mysql读取了原始数据,需要对数据进行加工、清洗,然后直接写入Mysql,这就涉及到了连接加工库、建库、建表、写数据的操作。
作者已经利用Python将数据写成固定格式的XML实现了。读者可根据自身项目或不同需求,按照不同的格式去创建自己的XML。
现在,作者将针对第二点,使用Python启动SQL语句来与MYSQL进行交互。
0.1 环境
本机环境:
- 系统: win 9
- Python版本: python3.6.4
- MySQL版本: 5.7.23
- Navicat Premium版本: 12.0
第三方包:
- pymysql
安装方法:
pip install pymysql
1 与Mysql交互
1.1 连接,获取游标
根据Mysql自身的规则,再进行写入、导出的时候,均需要先获取数据库的连接及执行游标。可以根据IP、密码、库名来连接:
conn = pymysql.connect(host='', user='', password='', charset='')
cursor = conn.cursor()
在实际工作中,可能要连接多个数据库,所以可以把host\user\psd等存储为字典:
def get_sql_conn(DB):
"""
通过字典的方式连接,有一点好处,就是需要连接多个库、表的时候,可以创建多个字典,好区分.
:return: 返回mysql数据库的连接与游标
"""
try:
conn = pymysql.connect(host=DB["host"], user=DB["user"], password=DB["password"], db=DB["db"],
charset=DB["charset"])
cursor = conn.cursor()
print("连接Mysql成功.")
except Exception as e:
conn = pymysql.connect(host=DB["host"], user=DB["user"], password=DB["password"], charset=DB["charset"])
cursor = conn.cursor()
print("连接Mysql失败:", e)
return conn, cursor
1.2 创建库
$emsp;在Mysql中创建一个自己的库,首先我们要明白,建库的SQL语句:
sql = 'CREATE DATABASE %s' % (dbname)
传达SQL命令到Mysql,则需要某个IP数据库的连接及执行游标进行:
cursor.execute(sql)
# conn.commit()
当然,你创建的这个库可能已经存在了,这时候要选择使用还是新建一个数据加工库:
def create_sql_db(self, conn, cursor, dbname):
"""
:param conn: 连接符
:param cursor: 游标
:param dbname: 需要创建的库名,str
:return: 打印创建成果
"""
try:
sql = 'CREATE DATABASE %s' % (dbname)
cursor.execute(sql)
print('创建库:\t{},成功.'.format(dbname))
sql = 'USE %s' % (dbname)
cursor.execute(sql)
print('使用当前库:\t{}.'.format(dbname))
except Exception as e:
print("库:\t'{}'已经存在.\t{}".format(dbname, e))
sql = 'USE %s' % (dbname)
cursor.execute(sql)
print('使用当前库:\t{}.'.format(dbname))
finally:
# conn.commit()
print('\n\n')
1.3 创建表
创建表基本思路和创建库的思路一致,基础建表SQL语句:
sql = 'CREATE TABLE tablename(
(colname1 INT(num) ... ,
colname2 VARCHAR(num) ... ,
colname3 FLOAT(num) ... ,
colname4 DATETIME(0) ... ,
)
注: datetime的字符长度,只能是0。
执行及默认:
cursor.execute(sql)
# conn.commit()
但是建表要比建库麻烦的多,因为涉及到了列的描述。例如,如果你有10w条5维的数据,如果要自动化建表的话,就需要:
- 获取这5列的名称
- 从第一列开始,获取10w条数据最多的类型,最大的长度(减少存储冗余)
- 组合列名 + 类型 + 长度
所以,我们先看下如何发现最多类型和最大长度:
-
最多类型,
可以通过内部自带isinstance(i,type)来计数。例如,对Int类型的数据进行计数:count_int = 0
for i in list:
if isinstance(i, int):
count_int += 1 -
最大长度:
根据UTF-8编码,’\u4e00’ <= s <= ‘\u9fff’ 之间为中文字符,中文字符占三个字节,英文字符一个字节,其他字符也是一个字节,来判断字符串的长度:if ‘\u4e00’ <= s <= ‘\u9fff’: # utf-8编码的中文区间
count_zh += 1
具体,寻找大数据量列表(10w级)存在最多数据类型和最大长度,详见下载所有完整代码。
建表的完整代码:
def create_sql_tb(conn, cursor, tablename, dataframe, col_limit=''):
"""
:param conn: 连接符
:param cursor: 游标
:param tablename: 创建的表名
:param dataframe: 按照数据框的情况,创建列名、列的长度
:param col_limit: 对列的其他限制
:return: 创建情况
"""
list_Type, list_Len = find_dfm_cols_most(dataframe)
print(list_Type, list_Len)
list_colname = list(dataframe.columns) # 新表的列名为数据框的列名
list_col_desc = [] # 列描述,sql语言
for i in range(len(list_colname)):
print('正创建列:\t{} '.format(list_colname[i]))
# col_limit = input('Please enter the limits of the column:\t')
col_desc = list_colname[i] + ' ' + list_Type[i] + '(' + str(list_Len[i]) + ')' + col_limit
list_col_desc.append(col_desc)
sql = 'CREATE TABLE %s' % (tablename) + '(' + ','.join(list_col_desc) + ')' # 用于执行的sql语句
sql = sql.replace('datetime(0)', 'datetime')
try:
# print(sql)
cursor.execute(sql)
except Exception as e:
print('表名:{}已存在.'.format(e))
select = input('选择是否覆盖建立该表(Y/N):')
if select == 'Y' or 'y':
sql1 = 'drop table if exists {}'.format(tablename) # 删除表
cursor.execute(sql1)
sql = 'CREATE TABLE %s' % (tablename) + '(' + ','.join(list_col_desc) + ')'
sql = sql.replace('datetime(0)', 'datetime')
cursor.execute(sql)
print('Rebuild table:\t{}'.format(tablename))
elif select == 'N'or'n':
print('Opt out.')
finally:
conn.commit()
1.4 插入数据
就像前面建库、建表一样,插入数据也需要Mysql某一数据库的连接和执行游标。插入的SQL语句:
INSERT INTO activity_rank_logs ('colname1','colname2',...) VALUES (58, 'str1', ...),
(69, 'str2', ...),
....
(85, 'strn', ...); # 插入多行语句
注:可以使用’’.jion(list),构造字符串语句:
list_columns = list(dataframe.columns) # 列名
insertSql = "INSERT INTO " + tablename + " (`" + "`,`".join(list_columns) + "`) VALUES " # 执行SQL语句的前半句
据测试,5000列为一次进行插入的效率最高,所以,判断如果数据大于5000,则按5000进行每次插入,但是如果数据小于5000,则仅插入一次:
def save_sql_data(conn, cursor, tablename, dataframe):
"""
5000条存储一次,加快存储速率
insert into tb (cols) values (),(),...()
:param conn: 连接符
:param cursor: 游标
:param tablename: 存入的表名
:param dataframe: 数据框
:return: 存储状态
"""
list_columns = list(dataframe.columns) # 列名
insertSql = "INSERT INTO " + tablename + " (`" + "`,`".join(list_columns) + "`) VALUES " # 执行sql前半句
valueStrings = [] # 值字符串
total_len = len(dataframe.values) # 取出是一个array,[[r1],[r2],[r3]...[rn]].n为多少列,取n.
count_extra = 0 # 余下的数据
count_insert = 5000 # 一次导入多少数据
count_mod = total_len % count_insert # 取余 5 % 3 = 2 ,还剩余多少数据
count_div = total_len // count_insert # 取模 5 // 3 = 1 ,5000的次数
print('数据共计: ', total_len)
if total_len > count_insert: # 1.数据框大5000列
for row in dataframe.values: # 每行
values = list(row) # 转列表
values = map(str, values) # 转列表中元素为字符
valueString = "('" + "','".join(values) + "')" # 每行用jion方法,凑成(e1,e2...er)
valueStrings.append(valueString) # valueStrings最大5000
if len(valueStrings) == count_insert:
insertSql_copy = insertSql
insertSql_copy += ",".join(valueStrings) # 塑造成存5000次的执行sql
valueStrings = [] # 值字符串清0,继续存
count_extra += 1 #
try:
# print(insertSql_copy)
cursor.execute(insertSql_copy) # 执行
print('Insert success!') # 插入一次成功,12w的数据,24次,1s. 1w2的数据,三次,0.08s。
except Exception as e:
print(e, '\n') # 执行错误
elif count_extra == count_div and len(valueStrings) == count_mod: # 判断是剩下的数据,不到5000也执行
insertSql_copy = insertSql
insertSql_copy += ",".join(valueStrings)
try:
# print(insertSql_copy)
cursor.execute(insertSql_copy) # 执行
print('Insert success!') # 插入一次成功,12w的数据,24次,1s. 1w2的数据,三次,0.08s。
except Exception as e:
print(e, '\n') # 执行错误
elif total_len <= count_insert: # 2.数据框小于5000行
for row in dataframe.values:
values = list(row)
values = map(str, values)
valueString = "('" + "','".join(values) + "')"
valueStrings.append(valueString)
insertSql_copy = insertSql
insertSql_copy += ",".join(valueStrings)
try:
print(insertSql_copy)
cursor.execute(insertSql_copy)
print('Insert success!')
except Exception as e:
print(e, '\n')
conn.commit()
print('插入次数:', count_extra, '截断', len(valueStrings))