Python实现MySql数据库导出表结构和数据功能-实例

文摘   科技   2023-11-11 13:38   北京  


ISEE小语


在网上看到马东在《奇葩说》中曾说的一句话:“一个人的情绪到了尽头,是沉默。”

在某个时候,沉默只是想放过自己!




回顾上篇


python中不常见却强大并好用的6个三方库,精简版

ISEE小栈,公众号:ISEE小栈python中不常见却强大并好用的6个三方库,精简版


开始本篇


导出数据库的表结构和数据,咱们经常使用的方式是在工具中直接导出,如在Navicat中[转储SQL文件]——>[结构和数据],可以很直接的导出sql文件。

那么有同学就会问到,直接用Navicat等这样的工具不就可以了么,为什么还要费劲的去用代码实现?

其实本次实际运用的场景是将正式环境的或是总库的数据库,快速地并批量的导出数据表结构和少量的数据,创建到新的数据库中,为测试自动化提供基础数据。

所以,这个是期初是和测试自动化结合使用的。也适用于运维备份数据库。总之开发、测试、运维、数据分析等有需用之。

此次实现功能具有特殊性和定制性,不同的数据库有可能需要有些特殊地处理。有借用到自己数据库的同学,如果有不成功的情况,那就需要根据实际情况在这基础上稍作优化




环境:

Pycharm

Python 3.9.18



安装:

pip install mysql-connector-python==8.1.0

pip install PyMySQL==1.1.0



导入:

import mysql.connector

import datetime



导出数据库的表结构和数据,分为三部分:

第一部分 生成删除表的SQL

第二部分 生成表的结构

第三部分 导出数据


在实现以上三个部分前,要先连接MySql原数据库,并获取数据库中所有的表名。


原数据库表:



连接MySql:

# 连接MySqldb_config = {    'host': '127.0.0.1',    'user': 'root',    'password': '123456',    'port': 3306,    'database': 'self_test'}cnx = mysql.connector.connect(**db_config)cursor = cnx.cursor()
# 查询所有表名cursor.execute("SHOW TABLES")table_names = cursor.fetchall()print(table_names)

结果:


看到把数据库表都输出了,说明已经连接成功~!


那么接下来就开始……



第一部分 

生成删除表的SQL


首要任务是生成删除表的SQL,防止在新库创建表的时候,有相同的表会有冲突的情况。


类似于Navicat工具,导出的SQL,删除表的操作也是在前面

for table_name in table_names:    table_name = table_name[0]    drop_sql = f"DROP TABLE IF EXISTS `{table_name}`;\n"    print(drop_sql)

(左右滑动查看完整代码)

结果:



第二部分 

生成表的结构


生成表的结构,我们需要:

  • 查询表结构信息,包括字段名、数据类型和注释

  • 查询主键信息

  • 查询索引信息

  • 查询表的字符集和排序规则


首先,组合表结构的字段名、数据类型和注释

for table_name in table_names:    table_name = table_name[0]    # 查询表结构信息,包括字段名、数据类型和注释    cursor.execute(f"SHOW FULL COLUMNS FROM {table_name}")    columns = cursor.fetchall()    # 查询索引信息    cursor.execute(f"SHOW INDEX FROM {table_name}")    indexes = cursor.fetchall()    # 查询主键信息    cursor.execute(f"SHOW KEYS FROM {table_name} WHERE Key_name = 'PRIMARY'")    primary_keys = cursor.fetchall()    # 查询表的字符集和排序规则    cursor.execute(f"SHOW TABLE STATUS LIKE '{table_name}'")    table_info = cursor.fetchone()
# 首先,组合表结构的字段名、数据类型和注释 create_sql = "" create_ = f"CREATE TABLE `{table_name}` (\n" create_sql += create_ for column in columns: column_name = column[0] # 字段名 data_type = column[1] # 字段类型 charset = column[2] # 字符集 Collation = f"CHARACTER SET {charset.split('_')[0]} COLLATE {charset}" if charset is not None else '' if column[3] == 'NO': is_Null = 'NOT NULL' if column[5] == '': Default = "DEFAULT ''" elif column[5] is not None: Default = f"DEFAULT '{column[5]}'" else: Default = '' else: is_Null = 'NULL' if column[5] is None: Default = 'DEFAULT NULL' elif column[5] == '': Default = "DEFAULT ''" elif column[5] == 'CURRENT_TIMESTAMP': # 判断时间类型的默认值 Default = "DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0)" else: Default = f"DEFAULT '{column[5]}'" # 字段注释 comment = column[8] # 判断sql字段最后一下后面是否有主键或和索引,如果没有,最后一个字段不加","号 if column == columns[-1] and len(indexes) == 0 and len(primary_keys) == 0: column_ = f" `{column_name}` {data_type} {Collation} {is_Null} {Default} COMMENT '{comment}'\n" create_sql += column_ else: column_ = f" `{column_name}` {data_type} {Collation} {is_Null} {Default} COMMENT '{comment}',\n" create_sql += column_
print(create_sql)

(左右滑动查看完整代码)

结果:

从以上结果可以看出,表中有主键或索引等信息,那么接下来把这些也补充上

主键,如果只有主键,添加完成后则结束;如果还有索引,则继续将索引等信息也添加

    # 如果只有主键,则结束    if len(indexes) == 1:        if len(primary_keys) > 0:            primary_key_columns = [pk[4] for pk in primary_keys]            primary_btree_columns = [pk[10] for pk in primary_keys]            primary_ = f"    PRIMARY KEY ({', '.join(primary_key_columns)}) USING {', '.join(set(primary_btree_columns))}\n"            create_sql += primary_    # 如果还有索引,则继续添加    else:        if len(primary_keys) > 0:            primary_key_columns = [pk[4] for pk in primary_keys]            primary_btree_columns = [pk[10] for pk in primary_keys]            primary_ = f"    PRIMARY KEY ({', '.join(f'`{pkc}`' for pkc in primary_key_columns)}) USING {', '.join(set(primary_btree_columns))},\n"            create_sql += primary_
# 添加索引 index_dict = {} for index in indexes: index_name = index[2] column_name = index[4] column_btree = index[10] if index_name not in index_dict: index_dict[index_name] = [] index_dict[index_name].append(column_name) # 获取所有的键 keys = list(index_dict.keys()) for index_name in list(index_dict.keys()): columns = index_dict[index_name] if index_name == 'PRIMARY': continue # 如果索引不是最后一个,在输出后面添加逗号 if index_name != keys[-1]: index_ = f" INDEX `{index_name}` ({', '.join(f'`{iname}`' for iname in columns)}) USING {column_btree},\n" create_sql += index_ # 如果索引是最后一个,不添加逗号 else: index_ = f" INDEX `{index_name}` ({', '.join(f'`{iname}`' for iname in columns)}) USING {column_btree}\n" create_sql += index_
print(create_sql)

(左右滑动查看完整代码)

结果:

以上结果,我们将主键信息和索引信息都获取到了。

那么最后需要获取数据表的引擎、字符集、表注释等信息了

    # 获取引擎、字符集、表注释等    ENGINE = table_info[1]    AUTO_INCREMENT = '' if table_info[10] is None else f"AUTO_INCREMENT = {table_info[10]}"    charset_table = table_info[14]  # 表字符集    charset_table_lst = charset_table.split('_')    CHARACTER = f'CHARACTER SET = {charset_table_lst[0]} COLLATE = {charset_table}'    row_format = table_info[16]    COMMENT = '' if table_info[17] == '' else f"COMMENT = '{table_info[17]}'"  # 表注释    engine_ = f") ENGINE = {ENGINE} {AUTO_INCREMENT} {CHARACTER} {COMMENT} {row_format};\n"    create_sql += engine_    print(create_sql)

(左右滑动查看完整代码)

结果:

以上创建表的SQL已成,接下来测试一下,我们新建个数据库,名字随意,在Navicat上将以上SQL执行一下

OK,成功


第三部分 

导出数据


上面导出表结构是备份数据库的必要操作,这部分导出数据是可选操作,如果只想导出数据库结构,这部分就不需要添加了。


本次原计划是导出每个表中最新的三条数据,来做基础数据。


换个直白的说法,就是查询最新的3条数据,生成insert语句。

这个查询的sql,可以是全部数据,也可以根据条件查询,总之用的时候自定义查询的sql语句即可。


for table_name in table_names:    table_name = table_name[0]    # 查询最新的3条数据    query = f"SELECT * FROM {table_name} order by id desc limit 3"    cursor.execute(query)
for row in cursor: values_list = [] for value in row: if isinstance(value, str): values_list.append(f"'{str(value)}'") elif value is None: values_list.append('NULL') elif isinstance(value, datetime.datetime): values_list.append(f"'{str(value.strftime('%Y-%m-%d %H:%M:%S'))}'") else: values_list.append(str(value)) # 使用逗号连接列表中的所有元素,得到最终的分隔字符串 values_str = ','.join(values_list) insert_sql = f"INSERT INTO {table_name} VALUES ({values_str});\n"
print(insert_sql)

(左右滑动查看完整代码)

结果:

已生成insert语句,接着第二部分创建的库和表,我们在Navicat中进行测试,看是否可以成功插入

OK,成功



总结 

统一归整与输出到文件


为了更方便的阅读,以上是分步记录的,最后会调整一下代码位置,按数据表结构更完整的输出来,并保存到新建的.sql文件中。方便后续自动化执行使用。


统一归整后的代码结构:


输出到SQL文件:


大家有没有发现,最终导出的这个sql文件内容,和Navicat中导出的格式相差无几? 


笔者把实例中使用的数据库和源码都共享了,有兴趣者,可试之!


后台回复“export_mysql”即可获取~!



寄语:世间三美,明月,清风,眼前……



看到这儿的朋友帮点个“”和“在看”,谢谢支持~!

     

文章就分享到这儿,喜欢就点个吧!




推荐阅读  点击标题可跳转

ISEE小栈
没有花里胡哨,简单才是王道。
 最新文章