Python知识分享:pymysql批量更新踩坑

前言

最近捣鼓项目时,pymysql用executemany方法批量更新大量数据过程中,对处理时长有点不满意,于是看了下源代码,发现里面实现批量update居然是循环去execute的,吃鲸。数据少其实没多大影响,如果大量数据批量执行,那数据库压力可想而知,难怪速度提不上来;于是乎,请教了下万能的百度,得到一个还算可以的解决方案–【创建临时表实现批量更新】,以下就原方法和新实现的方法写个示例做个对比。

先来看下pymysql的源码(cursors.py)

#: Regular expression for :meth:`Cursor.executemany`.
#: executemany only supports simple bulk insert.
#: You can use it to load large dataset.
RE_INSERT_VALUES = re.compile(
    r"\s*((?:INSERT|REPLACE)\b.+\bVALUES?\s*)"
    + r"(\(\s*(?:%s|%\(.+\)s)\s*(?:,\s*(?:%s|%\(.+\)s)\s*)*\))"
    + r"(\s*(?:ON DUPLICATE.*)?);?\s*\Z",
    re.IGNORECASE | re.DOTALL,
)
def executemany(self, query, args):
    if not args:
        return
    m = RE_INSERT_VALUES.match(query)
    if m:
        q_prefix = m.group(1) % ()
        q_values = m.group(2).rstrip()
        q_postfix = m.group(3) or ""
        assert q_values[0] == "(" and q_values[-1] == ")"
        return self._do_execute_many(
            q_prefix,
            q_values,
            q_postfix,
            args,
            self.max_stmt_length,
            self._get_db().encoding,
        )

    self.rowcount = sum(self.execute(query, arg) for arg in args)
    return self.rowcount

可以看到,executemany 只针对 INSERT|REPLACE、ON DUPLICATE 这些才能实现真正批量,而对于单纯的批量update 只能循环execute

准备

pip install pymysql

连接数据库

from typing import List
import pymysql
import time
from pymysql.cursors import DictCursor


dbconfig = {
    "host": "127.0.0.1",
    "user": "root",
    "password": "123456",
    "db": "testdb"
}

# 创建连接
conn = pymysql.connect(**dbconfig)

value = []
# 以4000条数据为例
for i in range(4000):
    value.append((str(i)+"name", str(i)+"ip", i))

executemany实现批量更新

# 批量更新
def updatemany(value:List):
    start_time = time.time()

    with conn.cursor(DictCursor) as cursor:
        sql = "UPDATE log SET name=%s,ip=%s WHERE id=%s"
        cursor.executemany(sql, value)
        conn.commit()

    end_time = time.time()
    print(f"【executemany】批量更新:用时{end_time-start_time}")
    
updatemany(value)

创建临时表 实现批量更新

# 批量更新(创建临时表更新)
def updatemanytemp(value:List):
    start_time = time.time()

    with conn.cursor(DictCursor) as cursor:
        # 创建临时表
        sql_temp = """
        CREATE TEMPORARY TABLE log_temp SELECT id,name,ip FROM log LIMIT 0
        """
        # 插入数据到临时表
        sql_insert = """
        INSERT INTO log_temp (name,ip,id)  VALUES (%s, %s, %s)
        """
        # 连表更新正式表
        sql_update = """
        UPDATE log, log_temp SET log.name=log_temp.name,log.ip=log_temp.ip WHERE log.id=log_temp.id
        """

        # 执行sql语句
        cursor.execute(sql_temp)
        cursor.executemany(sql_insert, value)
        cursor.execute(sql_update)
        conn.commit()


    end_time = time.time()
    print(f"【创建临时表 】批量更新:用时{end_time-start_time}")

updatemanytemp(value)

结果

在这里插入图片描述
从上图看到,2者相差10倍左右。这次示例的数据表只有3个字段4000条数据,所以这点时间看不出有多大影响,如果字段更多数据量更大,时间可能就不止相差这点了

最后

把方法写得通用一点,直接传递表名和参数即可,方便照搬

from typing import List
import pymysql
import time
from pymysql.cursors import DictCursor


dbconfig = {
    "host": "127.0.0.1",
    "user": "root",
    "password": "123456",
    "db": "testdb"
}

header = ["name","ip"] 
where = ["id"]
value = []
for i in range(4000):
    value.append((str(i)+"name", str(i)+"ip", i))

# 创建连接
conn = pymysql.connect(**dbconfig)


# 批量更新
def updatemany(table:str, header:List, where:List, value:List):
    start_time = time.time()

    with conn.cursor(DictCursor) as cursor:
        # 拼接set语句
        set_str = ",".join([f"{sql}=%s" for sql in header])
        # 拼接where条件
        where_str = " AND ".join([f"{sql}=%s" for sql in where])
        # 拼接整个sql语句
        sql = f"UPDATE {table} SET {set_str} WHERE {where_str}"

        # 执行sql语句
        cursor.executemany(sql, value)
        conn.commit()

    end_time = time.time()
    print(f"【executemany】批量更新:用时{end_time-start_time}")


# 批量更新(创建临时表更新)
def updatemanytemp(table:str, header:List, where:List, value:List):
    start_time = time.time()

    with conn.cursor(DictCursor) as cursor:
        # 拼接set语句
        set_str = ",".join([f"{table}.{sql}={table}_temp.{sql}" for sql in header])
        # 拼接where条件
        where_str = " AND ".join([f"{table}.{sql}={table}_temp.{sql}" for sql in where])
        # 拼接整个sql语句

        # 创建临时表
        sql_temp = f"""
        CREATE TEMPORARY TABLE {table}_temp SELECT {','.join(where + header)} FROM {table} LIMIT 0
        """
        # 插入数据到临时表
        sql_insert = f"""
        INSERT INTO {table}_temp ({','.join(header + where)})  VALUES {','.join([str(v) for v in value])}
        """
        # 连表更新正式表
        sql_update = f"""
        UPDATE {table}, {table}_temp SET {set_str} WHERE {where_str}
        """

        # 执行sql语句
        cursor.execute(sql_temp)
        cursor.execute(sql_insert)
        cursor.execute(sql_update)
        conn.commit()

    end_time = time.time()
    print(f"【创建临时表 】批量更新:用时{end_time-start_time}")

updatemanytemp("log", header, where, value)
updatemany("log", header, where, value)

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
可以使用pymysql库中的executemany()方法进行pymysql批量更新操作。该方法接受两个参数,第一个参数是更新一条的SQL语句模板,第二个参数是一个列表套元组的结构,每个元组表示一条数据的值。在执行executemany()方法时,会根据SQL语句模板和每个元组的值生成相应的更新语句,然后一次性执行这些更新语句。 以下是一个使用pymysql的executemany()方法进行批量更新的示例代码: ```python import pymysql def batch_update_to_mysql(update_list): mysql_db = pymysql.connect(host="HOST_IP", port=3306, user="username", password="password", database="db", charset="utf8") cursor = mysql_db.cursor() sql = "UPDATE table SET column1 = %s, column2 = %s WHERE id = %s" cursor.executemany(sql, update_list) mysql_db.commit() mysql_db.close() ``` 在上述代码中,我们首先建立了与MySQL数据库的连接,并创建了一个游标对象。然后,定义了更新一条数据的SQL语句模板,其中的占位符`%s`表示需要替换的值。接下来,通过执行`cursor.executemany()`方法,将SQL语句模板和更新数据的列表传入,实现批量更新操作。最后,通过执行`mysql_db.commit()`方法提交事务,并关闭数据库连接。 要注意的是,使用pymysql进行批量更新时,需要确保数据库连接是线程安全的。可以通过检查pymysql库中的`thread_safe`参数和`thread_safe()`函数来确认线程安全性。详情可以参考pymysql的官方文档。 希望以上信息对您有所帮助!<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python pymysql库的常用操作](https://download.csdn.net/download/weixin_38715048/13706442)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [Python 操作 pymysql 批量 增、删、改、查](https://blog.csdn.net/lyshark_lyshark/article/details/125847299)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值