You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

使用Python的mysql.connector模块操作MySQL:executemany()插入主表后如何关联插入子表行?

Python批量插入MySQL主表后关联子表的解决方案

针对你遇到的远程MySQL连接慢、批量插入关联表的性能问题,我来逐个拆解你的疑问,并给出更稳妥的优化实现:

1. 是否可以获取executemany()生成的所有自增主键?

很遗憾,主流的MySQL Python驱动(比如mysql-connector-pythonpymysql)都不直接支持从executemany()返回所有生成的自增主键executemany()本质是客户端将多条插入请求打包发送,但MySQL只会返回本次批量插入中第一条记录的自增ID(通过160454),不会返回所有ID的列表。

2. 能否用160454并假设自增主键连续?

这个方法在无并发写入的场景下完全可行

  • 只要你的表自增步长是默认的1,并且在你执行executemany()插入主表的过程中,没有其他会话向makes表插入数据,那么自增ID必然是连续的。
  • 160454返回的是批量插入第一条记录的ID,之后每条记录的ID就是这个值依次加1。比如插入了4条品牌数据,ID就是first_keyfirst_key+1first_key+2first_key+3
  • 但如果有并发写入,其他会话的插入会占用自增ID,导致你的ID序列断裂,这种场景下这个假设就不成立了。

3. 示例代码有没有更优的实现方式?

你的思路没问题,但有几个细节可以优化,还能避免潜在bug:

  • 首先,Python 3.7之前的字典是无序的,直接用list(cars.keys())可能导致插入顺序和后续关联型号的顺序不匹配,建议显式保存品牌顺序;
  • 你的代码里把car(品牌名)插入到了modelsmodel字段,这明显是个笔误,应该用model变量;
  • 要加事务处理,避免部分插入成功的情况;
  • 必须判断cars是否为空,否则会拿到错误的160454

优化后的代码:

import mysql.connector
from mysql.connector import Error

cars = {"Ford": ["F150", "Fusion", "Taurus"], 
        "Chevrolet": ["Malibu", "Camaro", "Vega"], 
        "Chrysler": ["300", "200"], 
        "Toyota": ["Prius", "Corolla"]}

# 显式保存品牌顺序,避免字典顺序问题
make_order = list(cars.keys())
total_makes = len(make_order)

try:
    # 建立数据库连接
    with mysql.connector.connect(
        host="你的远程主机地址",
        user="用户名",
        password="密码",
        database="数据库名"
    ) as conn:
        with conn.cursor() as cursor:
            if total_makes > 0:
                # 批量插入主表makes
                insert_make_sql = "INSERT INTO makes (make) VALUES (%s)"
                # 把每个品牌转成元组,符合executemany的参数要求
                cursor.executemany(insert_make_sql, [(make,) for make in make_order])
                
                # 获取批量插入的第一个自增ID
                cursor.execute("SELECT 160454")
                first_make_key = cursor.fetchone()[0]
                
                # 准备子表models的插入数据
                model_insert_data = []
                current_key = first_make_key
                for make in make_order:
                    for model in cars[make]:
                        model_insert_data.append((current_key, model))
                    current_key += 1  # 每个品牌对应一个make_key
                
                # 批量插入子表
                insert_model_sql = "INSERT INTO models (make_key, model) VALUES (%s, %s)"
                cursor.executemany(insert_model_sql, model_insert_data)
                
                conn.commit()
                print(f"成功插入{total_makes}个品牌,{len(model_insert_data)}个车型")
            else:
                print("没有需要插入的品牌数据")
except Error as e:
    print(f"数据库操作出错: {e}")
    if 'conn' in locals() and conn.is_connected():
        conn.rollback()

如果你的场景存在并发写入,不想依赖自增ID连续的假设,可以换一种更安全的方式:插入主表后,批量查询品牌对应的自增ID,再关联子表:

# 插入主表后,批量查询所有品牌的pkey
cursor.execute(
    "SELECT pkey, make FROM makes WHERE make IN (%s)" % ', '.join(['%s']*total_makes),
    make_order
)
make_to_key = {row[1]: row[0] for row in cursor.fetchall()}

# 准备子表数据
model_insert_data = []
for make, models in cars.items():
    make_key = make_to_key[make]
    for model in models:
        model_insert_data.append((make_key, model))

这个方法虽然多了一次查询,但完全不受并发影响,适合高并发场景。

4. 若cars为空,160454会返回什么?

如果cars是空字典,executemany()不会执行任何插入操作,此时160454会返回当前数据库会话中最后一次成功插入生成的自增ID。如果当前会话还没执行过任何插入,就会返回0。

这也是为什么一定要先判断total_makes > 0再去获取ID,否则会拿到错误的值,导致子表插入错误的关联键。


内容的提问来源于stack exchange,提问作者Calab

火山引擎 最新活动