
在数据处理和应用开发中,经常需要将程序中的数据集合批量导入到数据库中。使用循环结构遍历数据并逐条插入是常见的做法。然而,如果处理不当,这种看似简单的操作可能会引入逻辑错误或严重的安全漏洞。本教程将以python向postgresql插入数据为例,详细讲解如何规避这些问题,并采用专业且安全的实践方法。
一个常见的错误是在循环内部不当地重置计数器,导致只有部分数据被正确插入。考虑以下初始代码示例:
artist_name = ['Madonna', 'Slayer', 'Disturbed', 'Michael Jackson', 'Katty Parry']
# 假设 conn 已经是一个有效的数据库连接对象
with conn.cursor() as cur:
for artist in artist_name:
id_num = 0 # 错误:每次循环都将 id_num 重置为 0
id_num += 1 # 导致 id_num 每次都变为 1
cur.execute(f"""INSERT INTO Artist (Id, Name)
VALUES ('{id_num}', '{artist}')
ON CONFLICT DO NOTHING""");问题分析: 在这段代码中,id_num = 0 语句被放置在 for 循环的内部。这意味着在每次循环迭代开始时,id_num 都会被重新初始化为 0,紧接着又被 id_num += 1 语句递增到 1。结果是,所有尝试插入的记录都将使用相同的 Id 值(即 1)。
由于 Artist 表很可能将 Id 列定义为主键或唯一约束,当第一条记录成功插入 Id=1 后,后续所有尝试插入 Id=1 的操作都会触发 ON CONFLICT DO NOTHING 子句,导致这些记录被忽略。最终,只有列表中的第一个艺术家会被成功插入到数据库中。
要解决计数器重置的问题,只需将 id_num 的初始化移到循环的外部。这样,id_num 就能在每次迭代中持续递增,为每条记录生成唯一的 Id。
artist_name = ['Madonna', 'Slayer', 'Disturbed', 'Michael Jackson', 'Katty Parry']
# 假设 conn 已经是一个有效的数据库连接对象
with conn.cursor() as cur:
id_num = 0 # 正确:将 id_num 初始化移到循环外部
for artist in artist_name:
id_num += 1 # 每次循环递增,生成唯一的 Id
# SQL 查询部分待进一步优化(见下文)
# cur.execute(f"""INSERT INTO Artist (Id, Name)
# VALUES ('{id_num}', '{artist}')
# ON CONFLICT DO NOTHING""");通过这一修改,id_num 将按预期从 1 递增到 2,3,以此类推,确保每条记录都能获得一个唯一的标识符。
立即学习“Python免费学习笔记(深入)”;
尽管上述修改解决了计数器问题,但原始代码中直接使用 f-string 拼接 SQL 查询的方式,即 f"""... VALUES ('{id_num}', '{artist}') ...""",仍然存在严重的安全漏洞——SQL注入。
什么是SQL注入? SQL注入是一种常见的网络安全漏洞,攻击者通过在输入字段中插入恶意的SQL代码,来操纵数据库查询,从而绕过安全验证、窃取敏感数据,甚至破坏数据库。
为什么f-string拼接SQL不安全? 当使用 f-string 或其他字符串拼接方式构建SQL查询时,如果拼接的字符串来源于用户输入或其他不可信源,恶意用户可以构造特殊的字符串,这些字符串在被拼接到SQL查询后会改变查询的意图。即使在本例中 artist 列表是内部定义的,没有直接暴露给外部用户,但养成使用安全实践的习惯至关重要,以防止未来代码演变或重用时引入漏洞。
为了彻底杜绝SQL注入风险并提高代码的健壮性,强烈推荐使用参数化查询(Parameterized Queries)。参数化查询将SQL语句与参数值分开,数据库驱动程序会负责安全地将参数值绑定到SQL语句中,避免了字符串拼接带来的风险。
参数化查询的优势:
以下是使用参数化查询的完整代码示例,它同时解决了计数器问题和SQL注入风险:
import psycopg2 # 假设你正在使用 psycopg2 驱动
artist_names_list = ['Madonna', 'Slayer', 'Disturbed', 'Michael Jackson', 'Katty Parry']
# 示例:建立一个PostgreSQL连接(请替换为你的实际连接参数)
# conn = psycopg2.connect(
# host="your_host",
# database="your_database",
# user="your_user",
# password="your_password"
# )
# 假设 conn 已经是一个有效的数据库连接对象
# 为了演示,我们假设 conn 已经存在且配置正确
# 例如:
# conn = ... (通过 psycopg2.connect() 建立的连接)
try:
with conn.cursor() as cur:
id_counter = 0 # 正确管理循环计数器
for artist_name_item in artist_names_list:
id_counter += 1
cur.execute(
"""
INSERT INTO Artist (Id, Name)
VALUES (%(id_num)s, %(artist_name)s)
ON CONFLICT DO NOTHING;
""",
{'id_num': id_counter, 'artist_name': artist_name_item} # 使用字典传入命名参数
)
conn.commit() # 确保事务被提交,将更改保存到数据库
print("所有艺术家数据已成功插入数据库。")
except Exception as e:
conn.rollback() # 出现任何错误时回滚事务,撤销所有未提交的更改
print(f"数据插入失败:{e}")
finally:
if conn:
conn.close() # 关闭数据库连接,释放资源代码说明:
在Python中向PostgreSQL数据库批量插入数据时,务必注意以下两点:
遵循这些最佳实践,可以确保你的数据库操作既高效又安全,为应用程序奠定坚实的基础。
以上就是Python与PostgreSQL:循环批量插入数据的正确姿势与安全实践的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号