使用SQLite与discord.py开发Discord机器人遇OperationalError求助
问题排查:SQLite OperationalError: no such column 错误解决
我使用SQLite和discord.py开发Discord机器人时,set_ip命令执行报错:OperationalError: no such column: <传入的参数>,代码如下:
@bot.command() @commands.has_permissions(administrator=True) async def set_ip(ctx, arg=None): if arg == None: await ctx.send("You must type the IP address next to the command!") elif arg.endswith('.aternos.me') == False: await ctx.send('IP must end with .aternos.me') elif ctx.guild.id == None: await ctx.send("This is a guild-only command!") else: ipas = None id = ctx.guild.id conn.execute(f'''DROP TABLE IF EXISTS guild_{id}''') conn.execute(f'''CREATE TABLE IF NOT EXISTS guild_{id} ( ip TEXT NOT NULL )''') conn.execute(f'''INSERT INTO guild_{id} ("ip") VALUES ({arg})''') cursor = conn.execute(f'''SELECT ip FROM guild_{id}''') for row in cursor: ipas = row[0] if ipas == None: await ctx.send("Failed to set IP!") conn.execute(f'''DROP TABLE IF EXISTS guild_{id}''') else: await ctx.send(f"Your guild ip is now -> {ipas}") print("An ip has been set!")
错误原因
核心问题出在SQL字符串拼接:
conn.execute(f'''INSERT INTO guild_{id} ("ip") VALUES ({arg})''')
当arg是字符串(比如test.aternos.me)时,直接拼接后的SQL语句会变成:
INSERT INTO guild_12345 ("ip") VALUES (test.aternos.me)
SQLite会把test.aternos.me解析为列名(而非字符串值),自然会报错“不存在该列”。
解决方法
改用参数化查询处理字符串参数,同时优化代码逻辑:
修正后的完整代码
@bot.command() @commands.has_permissions(administrator=True) async def set_ip(ctx, arg=None): # 提前检查是否在服务器内执行 if not ctx.guild: await ctx.send("This is a guild-only command!") return if arg is None: await ctx.send("You must type the IP address next to the command!") return if not arg.endswith('.aternos.me'): await ctx.send('IP must end with .aternos.me') return guild_id = ctx.guild.id # 仅在表不存在时创建,无需每次删除重建 conn.execute(f'''CREATE TABLE IF NOT EXISTS guild_{guild_id} ( ip TEXT NOT NULL PRIMARY KEY )''') # 使用参数化查询插入/替换数据,避免语法错误与SQL注入 conn.execute(f'''INSERT OR REPLACE INTO guild_{guild_id} (ip) VALUES (?)''', (arg,)) # 提交事务,确保数据写入数据库 conn.commit() # 查询验证结果 cursor = conn.execute(f'''SELECT ip FROM guild_{guild_id}''') ip_row = cursor.fetchone() if ip_row: ipas = ip_row[0] await ctx.send(f"Your guild ip is now -> {ipas}") print("An ip has been set!") else: await ctx.send("Failed to set IP!") conn.execute(f'''DROP TABLE IF EXISTS guild_{guild_id}''') conn.commit()
关键修改点
- 参数化查询:将
VALUES ({arg})改为VALUES (?),并将参数放入execute的第二个元组参数中,SQLite会自动处理字符串的引号与转义,彻底避免语法错误。 - 新增主键与INSERT OR REPLACE:给
ip列添加PRIMARY KEY约束,使用INSERT OR REPLACE实现旧数据覆盖,无需每次删除表重建。 - 事务提交:执行插入操作后必须调用
conn.commit(),否则数据不会持久化到数据库。 - 逻辑前置优化:将服务器环境检查放在最前面,减少无效代码执行。
- 简化查询:用
cursor.fetchone()直接获取单行结果,替代循环遍历。
内容的提问来源于stack exchange,提问作者kagent263
相关产品推荐
相关产品推荐

