使用Python的pymysql包与Mysql进行交互——连接、建库、建表、插入数据

使用Python的pymysql包与Mysql进行交互——连接、建库、建表、插入数据

☆☆文档均为作者手写,大部分源码都已经贴上。
‘赠人玫瑰、手留余香’,如果这篇文档对您有帮助,请给作者点一个小红心,万分感谢。

0 背景及配置

0.1 背景

 作者接触到的某一项目,由于Python可使用库非常多、处理效率较高等优点。所以,在该项目运用到Python与Mysql进行了如下交互:

  1. 从爬虫、收集统计文献等渠道获取的数据要存入到项目原始数据库,可以利用Python与XML的交互将EXCEL或其他形式存储的数据转为固定好XML格式的数据,然后通过JAVA后端写好的平台,以XML格式的文件将数据传输至Mysql.
  2. 从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维的数据,如果要自动化建表的话,就需要:

  1. 获取这5列的名称
  2. 从第一列开始,获取10w条数据最多的类型,最大的长度(减少存储冗余)
  3. 组合列名 + 类型 + 长度

所以,我们先看下如何发现最多类型和最大长度:

  • 最多类型,
     可以通过内部自带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))

猜你喜欢

转载自blog.csdn.net/qq_40260867/article/details/86440685