SQLite中的变量表名

60 投票
10 回答
70243 浏览
提问于 2025-04-16 01:17

问题:有没有办法用变量来当表名,而不需要使用字符串构造器?


信息:

我现在正在做一个项目,目的是整理我一个星星模拟的数据。为此,我把所有数据都加载到一个sqlite数据库里。这个方法效果还不错,但我决定要让我的数据库更加灵活、高效和易用。我计划以后在模拟中添加一些小行星,并想为每颗星星建立一个表。这样的话,我就不用在一个有2000万个小行星的表里去查找每个太阳系里的1000到4000个小行星了。

有人告诉我,使用字符串构造器不好,因为这样会让我容易受到SQL注入攻击。虽然在这里这不是个大问题,因为只有我一个人能访问这些数据库,但我还是想遵循最佳实践。而且这样的话,如果我以后做一个类似的项目,开放给公众,我就知道该怎么做了。

目前我在这样做:

cursor.execute("CREATE TABLE StarFrame"+self.name+" (etc etc)")

这个方法有效,但我想做得更像:

cursor.execute("CREATE TABLE StarFrame(?) (etc etc)",self.name)

虽然我知道这可能是不可能的。不过我也能接受类似这样的做法:

cursor.execute("CREATE TABLE (?) (etc etc)",self.name)

如果这根本不可能,我会接受这个答案,但如果有人知道怎么做到,请告诉我。 :)

我用的是python编程。

10 个回答

9

我建议不要把数据分成多个表。如果你在星级这一列上创建一个索引,那么你就能很高效地访问这些数据,不会遇到任何问题。

13

对于那些想把表名当作变量的人,我从另一个回答中找到了这个方法,链接在这里

内容如下,来自mhawke的引用:

你不能用参数替换来设置表名。你需要自己把表名加到查询字符串里。可以这样做:

query = 'SELECT * FROM {}'.format(table)
c.execute(query)

需要注意的是,表名的来源。如果这个表名来自不可信的地方,比如用户输入,那么你需要验证这个表名,以避免可能的SQL注入攻击。一个方法是构建一个参数化查询,从数据库目录中查找表名:

import sqlite3

def exists_table(db, name):
    query = "SELECT 1 FROM sqlite_master WHERE type='table' and name = ?"
    return db.execute(query, (name,)).fetchone() is not None
60

很遗憾,表格不能作为参数替换的对象(虽然我没有找到确凿的来源,但在一些网络论坛上见过这样的说法)。

如果你担心注入问题(你可能应该担心),可以写一个函数来清理字符串,然后再传递给数据库。因为你只需要一个表名,所以只要接受字母和数字就可以了,去掉所有的标点符号,比如 )(][;, 和空格。简单来说,就是只保留 A-Z a-z 0-9 这些字符。

def scrub(table_name):
    return ''.join( chr for chr in table_name if chr.isalnum() )

scrub('); drop tables --')  # returns 'droptables'

撰写回答