You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django自定义命令执行LOAD DATA INFILE导入MySQL报1064错误

报错排查思路

这个1064语法错误核心是SQL拼接逻辑存在硬伤,按以下优先级排查即可:

  • 直接触发报错的问题:LOAD DATA INFILE '%s' 中手动给占位符加了单引号。Django的cursor参数化执行逻辑会自动为字符串类型参数添加边界引号、转义特殊字符,手动加单引号会最终生成LOAD DATA INFILE ''/your/path/file.txt''这类双重引号的非法SQL,报错指向path位置就是这个原因。
  • 第二处语法错误:库名、表名属于SQL标识符,不能通过%s占位符传参。MySQL预处理语法仅支持值类型的参数占位,标识符(库名、表名、字段名)走%s传参会被自动转义为带引号的字符串,最终生成INTO TABLE 'your_db.your_table'的非法语法,表名会被解析成字符串值而非合法标识符。
  • 隐藏逻辑bug:glob匹配规则*[!.sql]写法错误。方括号内的字符集是单字符匹配规则,这个写法实际是匹配文件名最后一个字符不是.、s、q、l的文件,完全实现不了「排除.sql后缀文件」的需求,很容易漏扫目标txt文件、或者把非目标文件纳入导入范围。
  • 权限适配问题:如果使用MySQL 8.x默认安全配置,读取客户端本地文件需要用LOAD DATA LOCAL INFILE而非LOAD DATA INFILE——后者默认要求文件存储在MySQL服务端宿主机器上且匹配系统权限,手动在MySQL终端执行成功一般是因为终端客户端默认开启了local_infile支持,代码里不加LOCAL关键字会被MySQL拒绝读取本地文件。
修正后的Django自定义命令实现

修复上述所有问题,补充基础校验和异常提示,代码如下:

import glob
import os
from django.core.management.base import BaseCommand
from django.db import connections
from django.conf import settings

class Command(BaseCommand):
    help = '批量导入指定目录下的txt文件到MySQL数据库'

    def add_arguments(self, parser):
        parser.add_argument('-p', required=True, type=str, help='待导入文件所在目录路径')

    def handle(self, *args, **options):
        self.stdout.write("\nStarting import task...")
        target_dir = options["p"]
        db_name = settings.DATABASES['wk']['NAME']
        # 修正glob规则:直接匹配目录下所有txt后缀文件,天然排除.sql等其他格式文件
        file_list = glob.glob(os.path.join(target_dir, '*.txt'))
        if not file_list:
            self.stdout.write(self.style.WARNING("指定目录下未找到待导入的txt文件,任务退出"))
            return

        with connections['wk'].cursor() as cursor:
            # 开启会话级本地文件读取权限,适配客户端侧文件导入场景
            cursor.execute("SET SESSION local_infile = 1")
            for file_path in file_list:
                file_name = os.path.basename(file_path)
                # 提取无后缀文件名作为表名,统一转小写
                table_name_raw = os.path.splitext(file_name)[0].lower()
                # 校验表名合法性,避免特殊字符引发语法错误或注入风险
                if not table_name_raw.isidentifier():
                    self.stderr.write(self.style.ERROR(f"跳过非法文件:{file_name},文件名包含不符合MySQL表名规则的字符"))
                    continue
                # 给库名、表名加反引号,兼容保留字、特殊命名场景
                full_table_name = f"`{db_name}`.`{table_name_raw}`"
                try:
                    # 文件路径作为值类型走参数化传参,不加手动引号;表名完成校验后直接拼接
                    # 如有字段分隔符、换行符、跳过表头的需求,直接在表名后补充 FIELDS TERMINATED BY、LINES TERMINATED BY、IGNORE 1 LINES 等配置即可,和终端执行语法完全一致
                    sql = "LOAD DATA LOCAL INFILE %s INTO TABLE %s"
                    cursor.execute(sql, [file_path, full_table_name])
                    self.stdout.write(self.style.SUCCESS(f"成功导入文件:{file_name} -> 表{full_table_name}"))
                except Exception as e:
                    self.stderr.write(self.style.ERROR(f"导入文件{file_name}失败:{str(e)}"))
        self.stdout.write(self.style.SUCCESS("所有文件导入任务执行完成"))

如果你手动在终端执行LOAD DATA时配置过特殊字段分隔符、行分隔符、忽略首行、字段转义规则,直接把对应参数补到LOAD DATA语句的表名后即可,和终端执行的语法保持完全一致就不会出错。

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 18:39:35