pymysql和pandas组合灵活实现数据库操作

背景说明

​ 在做数据分析工作过程中,经常会有访问数据库和数据处理的场景。访问数据库通常可以使用python的数据包,如pymysql,cx_Oracle等;数据处理可以采用pandas来进行,但是在使用的时候,总是有不顺手的地方。比如:

  1. pymysql、cx_oracle等返回的结果并不是DataFrame结构,还是需要自己转换。
  2. pymysql、cx_oracle等在做数据库写入的时候,需要再写获取连接、游标等,操作比较繁琐。
  3. pymysql、cx_oracle等需要再代码中编写数据库账号密码,这样会有不安全的情况。
  4. pandas虽然能访问数据库,没办法做数据删除的操作。
  5. pandas没有办法执行mysql的插入更新操作等。

基于此,根据自己的实际工作情况,编写属于自己的数据库访问的python库是比较好的。

代码说明

话不多说,先上代码为敬:

编写如下代码,命名为PySQL.py作为自己的库文件。以下代码实现的是MySQL库的访问,其他数据库需要更换导入的数据库的库文件。

import pymysql
import numpy as np
import pandas as pd

class DB1(object):
    def __init__(self) -> None:
        self.conn = pymysql.connect(user = "db_user",
            password = "db_passwd",
            port = 3306,
            host = 'xxx.xxx.xxx.xxx',
            database ="database1")
    
    def query_data(self, sql):
        cursor = self.conn.cursor()
        cursor.execute(sql)
        data = cursor.fetchall()
        # pandas 版本过低语法
        # data = list(data)
        description = cursor.description
        fields = [item[0] for item in description]
        df_data = pd.DataFrame(data, columns=fields)
        return df_data
    
    def execute(self, sql, data=None):
        """
        可以执行INSERT和UPDATE
        建议采用防止sql注入的方式执行
        把数据字段放到参数里。
        """
        cursor = self.conn.cursor()
        if data is None:
            cursor.execute(sql)
        else:
            cursor.execute(sql, data)
        self.conn.commit()
    
    def insert_dataframe(self, table_name, df_data:pd.DataFrame, columns=None):
        # df_data.fillna("$$$$$$$$$$$$", inplace=True)
        # print(df_data)
        # print("****************")
        # df_data.replace("$$$$$$$$$$$$", "##########", inplace=True)
        # print(df_data)
        df_data = df_data.where(df_data.notnull(), None)
        if columns is None:
            columns = df_data.columns.values.tolist()
        sql_insert = """
            INSERT INTO {table_name} ({col_list}) VALUES({data_holder})
        """.format(table_name=table_name,
                   col_list=",".join(columns),
                   data_holder=",".join(["%s" for _ in columns])
                   )
        data = df_data.values.tolist()
        cursor = self.conn.cursor()
        cursor.executemany(sql_insert, data)
        cursor.close()
        self.conn.commit()
    
    def insert_list(self, table_name, columns, data:list):
        sql_insert = """
            INSERT INTO {table_name} ({col_list}) VALUES({data_holder})
        """.format(table_name=table_name,
                   col_list=",".join(columns),
                   data_holder=",".join(["%s" for _ in columns])
                   )
        cursor = self.conn.cursor()
        cursor.executemany(sql_insert, data)
        cursor.close()
        self.conn.commit()
    
    def close(self):
        return self.conn.close()
    
    def __del__(self):
        try:
            print("执行析构函数")
            self.conn.close()
        except:
            pass
        return None
    
class DB2(DB1):
    def __init__(self) -> None:
        super().__init__()
        self.conn = pymysql.connect(user = "db_user",
            password = "db_passwd",
            port = 3306,
            host = 'xxx.xxx.xxx.xxx',
            database ="database2")

在编写的类中,主要包含四个方法,分别如下:

  1. 【query_data】:这个方法主要是将读到的数据,转换为DataFrame,方便使用pandas进行数据处理操作。fetch_all返回的原本是二维的list结构,这里通过读取字段信息,将list转换为了DataFrame格式。
  2. 【execute】:这个可以执行正常的SQL语句,通过这层封装,其实是做了一个简易的SQL客户端。实际在应用的过程中,主要执行DELETE语句。
  3. 【insert_list】:能够将二位表的数据,插入到数据库中。因为list本身不包含字段信息,这里入参除了数据和插入表的表名外,还需要指定字段信息columns,并且,columns中的字段顺序和data里的数据的位置顺序一致。
  4. 【insert_dataframe】这个方法是将DataFrame结构插入到数据库当中。DataFrame本身包含列的信息,如果不指定columns,执行时,是按照DataFrame的列信息往数据库中插入数据,这个时候,要保证DataFrame的列信息和要写入的数据库表的列信息是一致的,或者是数据库表的子集。
  5. 其他说明:
    1. 除了以上方法外,还有初始化方法和del方法。初始化方法为了构建数据库连接;del方法为了在程序正常退出或者异常退出时,关闭连接,不占用连接资源。
    2. 当想再生成另一个库的的类时,可以直接继承编写,把初始化方法覆盖即可。
    3. 我不建议在初始化方法里传入数据库密码来生成连接。一方面,使用时,特别是多人使用时,需要传数据库密码,不安全。另一方面,如果数据库密码有修改,在调用地方都得修改,这样不友好。

代码使用

使用时,可以直接导入PySQL包,然后实例化类,调用上述方法,执行数据操作。示例如下:

from PySQL import DB1, DB2
import pandas as pd
from datetime import datetime
import random
if __name__=="__main__":
    o = DB1()
    df_data = pd.read_excel("./file.xlsx",dtype="object")
    cols = [
        "field1", # field1
        "field2", # field2
        "field3", # field3
        "field4",  # field4
        "UPT_TIME" # 更新时间

    ]

    insert_cols = df_data.columns.tolist() + ["UPT_TIME"]
    df_data["UPT_TIME"] = datetime.now()
    df_data = df_data[insert_cols]
    o.execute("truncate table table_1")
    o.insert_dataframe(table_name="table_1",
                  df_data=df_data,
                  columns=cols)
    # print(df_data.columns)

当然,也可以直接当成SQL客户端工具用直接写SQL。如下SQL完成从表2中取两个字段,放入表1中。注意,as后面的名称要在表1中出现。

from PySQL import DB1, DB2
import pandas as pd
from datetime import datetime
import random
if __name__=="__main__":
    o = DB1()
    sql = """
    	select f1 as field1, f2 as field2 from table_2
    """
    df_data = o.query_data(sql)

    insert_cols = df_data.columns.tolist() + ["UPT_TIME"]
    df_data["UPT_TIME"] = datetime.now()
    df_data = df_data[insert_cols]
    o.execute("truncate table table_1")
    o.insert_dataframe(table_name="table_1",
                  df_data=df_data,
                  columns=cols)
    # print(df_data.columns)

补充说明

为了适应更多的场景,还可以通过添加更多逻辑,以适应更多的场景。比如:

  1. insert方法里分多次插入,以提高插入效率,避免长时间锁表。

  2. 可以添加try…catch语句,这样可以自定义异常输出。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值