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

Python sqlite3如何动态指定表名?使用占位符报错求助

问题:动态创建SQLite表时使用占位符报错

我想在函数中每次创建不同名称的表,尝试使用?占位符实现,但运行时报错。

我的代码

@bot.tree.command(name='create', description='Crea un nuovo torneo')
@app_commands.describe(name='Il nome del torneo', max_participants='Numero massimo di partecipanti', 
                       date='Data del torneo(mm:hh:dd:MM:yyyy)', mode='Modalità del torneo', 
                       mappa='Mappa del torneo')
async def create(interaction:discord.Interaction, name:str, max_participants:int, date:str, 
                 mode:str, mappa:str):
    date = datetime.datetime(year=int((date.split(':'))[4]), month=int((date.split(':'))[3]), 
                             day=int((date.split(':'))[2]), hour=int((date.split(':'))[1]), 
                             minute=int((date.split(':'))[0]), second=0)
    conn = sqlite3.connect('tourneys.sqlite3')
    c = conn.cursor()
    c.execute('CREATE TABLE IF NOT EXISTS (?) (id INTEGER PRIMARY KEY, max_participants INTEGER, date DATETIME, mode TEXT, map TEXT)',
              (name, )) # error here
    t_id = (random.randint(0,9) for _ in range(9))
    c.execute('INSERT INTO ? VALUES (?, ?, ?, ?, ?)', (name, t_id, max_participants, date, mode, mappa))
    c.execute('SELECT * FROM ? WHERE id = ?', (name, t_id))
    t = c.fetchone()
    await interaction.response.send_message(t)

错误信息

Error: Command 'create' raised an exception: OperationalError: near "(": syntax error

我知道不能用占位符处理列/表名,但见过有人使用.format实现,却不理解原理。我原本期望通过占位符创建对应名称的表,但未能成功。


解决方案

为什么占位符不行?

SQLite的?占位符是参数化查询的一部分,它仅用于安全传递数据值(比如插入的数字、字符串),不能用来替换SQL语句中的标识符(表名、列名这类属于SQL语法结构的部分)。数据库会把占位符当成数据处理,直接用(?)作为表名会触发语法错误。

用.format实现的原理

.format是Python的字符串格式化方法,它会直接将表名字符串拼接到SQL语句中,生成符合语法规范的完整SQL指令。比如传入name="tourney1",格式化后SQL语句会变成CREATE TABLE IF NOT EXISTS tourney1 (...),这样数据库就能识别出正确的表名。

修改后的代码

注意:使用字符串格式化时必须严格校验输入的表名,避免SQL注入风险(比如用户输入恶意字符串破坏数据库)。这里可以先对name做简单过滤,只允许字母、数字和下划线:

import re
import sqlite3
import datetime
import random
import discord
from discord import app_commands

@bot.tree.command(name='create', description='Crea un nuovo torneo')
@app_commands.describe(name='Il nome del torneo', max_participants='Numero massimo di partecipanti', 
                       date='Data del torneo(mm:hh:dd:MM:yyyy)', mode='Modalità del torneo', 
                       mappa='Mappa del torneo')
async def create(interaction:discord.Interaction, name:str, max_participants:int, date:str, 
                 mode:str, mappa:str):
    # 校验表名:仅允许字母、数字、下划线
    if not re.match(r'^[a-zA-Z0-9_]+$', name):
        await interaction.response.send_message("Nome torneo non valido: solo lettere, numeri e underscore sono permessi")
        return
    
    # 解析日期并处理异常
    date_parts = date.split(':')
    try:
        date = datetime.datetime(
            year=int(date_parts[4]),
            month=int(date_parts[3]),
            day=int(date_parts[2]),
            hour=int(date_parts[1]),
            minute=int(date_parts[0]),
            second=0
        )
    except (IndexError, ValueError):
        await interaction.response.send_message("Formato data non valido: usa mm:hh:dd:MM:yyyy")
        return
    
    # 生成可存入数据库的ID(原生成器对象无法直接存储,转成字符串)
    t_id = ''.join(str(random.randint(0,9)) for _ in range(9))
    
    conn = sqlite3.connect('tourneys.sqlite3')
    c = conn.cursor()
    
    # 用字符串格式化替换表名,创建表
    create_table_sql = 'CREATE TABLE IF NOT EXISTS {} (id TEXT PRIMARY KEY, max_participants INTEGER, date DATETIME, mode TEXT, map TEXT)'.format(name)
    c.execute(create_table_sql)
    
    # 插入数据:表名用格式化,数据值仍用占位符保证安全
    insert_sql = 'INSERT INTO {} VALUES (?, ?, ?, ?, ?)'.format(name)
    c.execute(insert_sql, (t_id, max_participants, date, mode, mappa))
    
    # 查询数据:同理处理表名和参数
    select_sql = 'SELECT * FROM {} WHERE id = ?'.format(name)
    c.execute(select_sql, (t_id,))
    t = c.fetchone()
    
    conn.commit()  # 提交事务,确保数据写入数据库
    conn.close()
    
    await interaction.response.send_message(str(t))

额外注意点

  1. 事务提交:原代码缺少conn.commit(),插入的数据不会持久化到数据库,必须添加。
  2. ID生成:原代码生成的是生成器对象,无法直接存入数据库,需转换为字符串或整数。
  3. SQL注入防护:必须限制表名的字符范围,避免用户输入类似tourney; DROP TABLE users;的恶意字符串。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 07:35:34