
本文介绍一种基于 ast.literal_eval 的稳健方法,从含嵌套逗号、引号和括号的 SQL INSERT 语句中准确提取字段名与对应值,生成字典列表,避免正则或简单字符串分割导致的解析错误。
本文介绍一种基于 ast.literal_eval 的稳健方法,从含嵌套逗号、引号和括号的 sql insert 语句中准确提取字段名与对应值,生成字典列表,避免正则或简单字符串分割导致的解析错误。
在数据库迁移场景中,常需从原始 .sql 文件(含 CREATE TABLE 和 INSERT INTO ... VALUES (...))中提取插入数据,并映射到新表结构。但直接用 split(',') 解析 VALUES 子句极易出错——因为字段值本身可能包含逗号(如 'Sport,Food,Creature')、单引号、括号甚至换行,传统文本处理难以可靠识别边界。
推荐采用 Python 内置的 ast.literal_eval 模块:它能安全地将符合 Python 字面量语法的字符串(如元组、列表、字符串、数字)解析为对应对象,且不执行任意代码,比 eval() 更安全。关键在于将 SQL 片段规范化为合法 Python 字面量格式。
✅ 核心思路三步走
-
提取字段名:从
INSERT INTO table (col1, col2, ...)中获取列名列表; -
提取值元组:将
VALUES (..., ...), (..., ...);部分转换为 Python 元组列表; -
组合为字典列表:用
zip(fieldnames, row)将每行值与字段名配对生成字典。
? 示例代码(完整可运行)
import ast
import re
def parse_sql_inserts(sql_content: str) -> list:
"""
从 SQL 字符串中提取所有 INSERT 语句的字段与值,返回字典列表。
支持多行 VALUES、含逗号/引号/括号的字段值。
"""
# 提取所有 INSERT INTO ... VALUES (...) 块(支持跨行)
insert_pattern = r"INSERT\s+INTO\s+`?(\w+)`?\s*\(`?([^`)]+)`?\)\s*VALUES\s*([\s\S]*?);"
results = []
for match in re.finditer(insert_pattern, sql_content, re.IGNORECASE | re.MULTILINE):
# 步骤1:解析字段名(去除反引号,按逗号分割后清理空格)
columns_raw = match.group(2)
fieldnames = [col.strip().strip('`\'"') for col in columns_raw.split(',')]
# 步骤2:标准化 VALUES 部分 → 转为 Python 元组列表
values_part = match.group(3).strip()
# 替换 SQL 反引号为单引号(ast.literal_eval 仅认 Python 引号)
values_clean = values_part.replace('`', "'")
# 确保以逗号结尾的行被正确连接(移除换行+多余空格,保留内部结构)
values_clean = re.sub(r'\s+', ' ', values_clean).strip()
# 包裹成合法 Python 表达式:[(...), (...)]
if not values_clean.startswith('['):
values_clean = f"[{values_clean}]"
try:
# 安全解析为 Python 元组列表
rows = ast.literal_eval(values_clean)
except (SyntaxError, ValueError) as e:
raise ValueError(f"无法解析 VALUES 部分:{values_part[:100]}... 错误:{e}")
# 步骤3:构建字典列表
for row in rows:
if not isinstance(row, tuple):
row = tuple(row) # 兼容单行或列表格式
if len(row) != len(fieldnames):
raise ValueError(f"字段数({len(fieldnames)})与值数({len(row)})不匹配:{row}")
results.append(dict(zip(fieldnames, row)))
return results
# 使用示例
sql_text = """
INSERT INTO `examle` (`myid`, `title`, `value`, `usefull`, `picture`, `short_description`, `description`, `wikipedia`, `category`, `date`) VALUES
(1, 'Cat', '3,45', 'No', 'cat.jpg', 'A very useless domestic cat.', 'The cat (Felis catus), also known as the domestic cat or housecat to distinguish it from other felines and felids, is a small carnivorous mammal that is valued by humans for its companionship and its ability to hunt vermin and household pests. It has been associated with humans for at least 9,500 years and is currently the most popular pet in the world.', 'http://en.wikipedia.org/wiki/Cat', 'Sport,Food,Creature', '2009-10-27 16:37:49'),
(3, 'Basketball', '9,45', 'Yes', 'basketball.jpg', 'A Baskeball sport utility.', 'Basketball is a team sport in which two teams of 5 players try to score points against one another by placing a ball through a 10 foot (3.048 m) high hoop (the goal) under organized rules. Basketball is one of the most popular and widely viewed sports in the world.', 'http://en.wikipedia.org/wiki/Basketball', 'Sport', '2009-10-27 16:36:39');
"""
data = parse_sql_inserts(sql_text)
print(f"成功解析 {len(data)} 条记录")
for i, record in enumerate(data, 1):
print(f"第{i}条:{record['title']} → category='{record['category']}'")
⚠️ 注意事项与局限性
-
安全性保障:
ast.literal_eval仅解析基础字面量(字符串、数字、元组、列表、字典、布尔值、None),杜绝代码注入风险; -
SQL 格式要求:需确保
VALUES部分语法接近 Python 元组格式(如括号包围、逗号分隔),实际中多数导出工具(如 phpMyAdmin)生成的 SQL 符合此规范; -
不支持复杂表达式:若
VALUES中含函数调用(如NOW())、变量或子查询,则需先预处理为字面量; -
编码与转义:原始 SQL 若含 Unicode 或特殊转义(如
\n),需确保文件读取时指定正确编码(如utf-8),且ast.literal_eval能正确处理; -
批量处理建议:对大型
.sql文件,应逐块读取(而非一次性加载),配合re.finditer流式提取,避免内存溢出。
该方法平衡了准确性与简洁性,是处理结构化 SQL 数据迁移任务的实用首选方案。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











