【问题标题】:Variable table name in sqlitesqlite中的变量表名
【发布时间】:2010-07-14 14:34:39
【问题描述】:

问题:是否可以使用变量作为表名而不必使用字符串构造函数?


信息:

我现在正在开展一个项目,该项目对来自我的恒星模拟的数据进行分类。为此,我将所有数据加载到 sqlite 数据库中。它工作得很好,但我决定为我的数据库增加更多的灵活性、效率和可用性。我计划稍后将小行星添加到模拟中,并希望为每颗恒星提供一个表格。这样我就不必为每个太阳系中的 1-4k 查询 20m 一些小行星的表。

有人告诉我,使用字符串构造函数很糟糕,因为它让我容易受到 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 编码。

【问题讨论】:

    标签: python database sqlite


    【解决方案1】:

    很遗憾,表格不能作为参数替换的目标(我没有找到任何确定的来源,但我在一些网络论坛上看到过)。

    如果您担心注入(您可能应该担心),您可以编写一个函数,在传递字符串之前对其进行清理。由于您只是在寻找一个表名,因此您应该安全地接受字母数字,去掉所有标点符号,例如 )(][;, 和空格。基本上,只需保留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'
    

    【讨论】:

    • 所以这是不可能的。这就是我需要知道的。而且你提出的擦洗方法看起来很不错。谢谢!
    • 就个人而言,如果看到这样的字符,我会抛出异常。他们一定不在那里,所以出了点问题,我宁愿知道。
    • -1;这个答案的细节似乎有缺陷。它过于严格(例如,不允许在表名中使用下划线,这是相当常见的),并且正如@JanHudec 指出的那样,如果scrub() 实际上删除了任何字符,它几乎肯定会破坏查询;这应该会导致立即抛出异常,而不是等待查询执行时间来发现。
    • 问题是参数只能替换为值,不能替换其他标识符或 SQL 的一般位。这意味着它们很容易注入到查询的字节编码版本中。更一般地替换需要重新编译查询(是的,即使只是更改表或列名),所以 SQLite 引擎说“不”,你需要用外部语言来做。
    • 我也会保留下划线。
    【解决方案2】:

    对于寻找将表格作为变量的方法的人,我从另一个回复相同的问题here 中得到这个:

    它说了以下内容并且有效。全部引自mhawke

    您不能对表名使用参数替换。您需要自己将表名添加到查询字符串中。像这样的:

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

    需要注意的一点是表名的值的来源。如果那来自不受信任的来源,例如一个用户,那么您需要验证表名以避免潜在的 SQL 注入攻击。一种方法可能是构造一个参数化查询,从 DB 目录中查找表名:

    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
    

    【讨论】:

    • 当表可能存在时,exists_table 是一个很好的参数化检查;但是它在 OP 的 create table 问题中效果不佳......
    【解决方案3】:

    我不会将数据分成多个表。如果您在星列上创建索引,则高效访问数据不会有任何问题。

    【讨论】:

    • 我很难相信。据我所知,sqlite 会遍历表并检查我想要的索引值是否与星号的索引值相同。如果它们是,它会选择其他方式,它会继续并忽略它。那就是我必须遍历数百万个条目才能找到所需的信息。从处理器的角度来看,这没什么大不了的。但我的硬盘将无法跟上。
      还有,我会在已经很大的文件中添加 8*(小行星数量)字节。我不喜欢那样的声音。
    • 您需要了解更多关于 SQLite 和所有关系数据库如何工作的信息。他们使用索引来快速找到您想要的行。您所描述的称为“全表扫描”,是的,这很可怕。但很容易避免。人们一直在创建具有数百万行的表,并且永远不必承担全表扫描的成本。
    • +1 用于澄清索引的值以避免全表扫描。
    • 啊。所以这不像是存储的实际数据中的标识符?这更有意义。是的,你是对的。我确实需要了解更多。我无法争辩,因为我大约 3 周前才开始使用 sqlite。 >.
    • 索引是一个独立于表中行的结构。您可以使用键和索引快速找到感兴趣的行。
    【解决方案4】:

    尝试字符串格式:

    sql_cmd = '''CREATE TABLE {}(id, column1, column2, column2)'''.format(
                'table_name')
    db.execute(sql_cmd)
    

    用你的愿望替换'table_name'

    【讨论】:

    • 不推荐这样做,因为它会使您容易受到 SQL 注入攻击。
    【解决方案5】:

    为了避免硬编码表名,我使用了:

    table = "sometable"
    c = conn.cursor()
    
    c.execute('''CREATE TABLE IF NOT EXISTS {} (
                    importantdate DATE,
                    somename VARCHAR,
                    )'''.format(table))
    
    c.execute('''INSERT INTO {} VALUES (?, ?)'''.format(table),
                      (datetime.strftime(datetime.today(), "%Y-%m-%d"),
                       myname))
    

    【讨论】:

      【解决方案6】:

      正如在其他答案中所说,“表不能成为参数替换的目标”但是如果您发现自己处于没有选择的绑定中,这是一种测试提供的表名是否为的方法有效。
      注意:我已将表名设为真正的猪,以尝试涵盖所有基础。

      import sys
      import sqlite3
      def delim(s):
        delims="\"'`"
        use_delim = []
        for d in delims:
         if d not in s:
          use_delim.append(d)
        return use_delim
      
      db_name = "some.db"
      db = sqlite3.connect(db_name)
      mycursor = db.cursor()
      table = 'so""m ][ `etable'
      delimiters = delim(table)
      if len(delimiters) < 1:
          print "The name of the database will not allow this!"
          sys.exit()
      use_delimiter = delimiters[0]
      print "Using delimiter ", use_delimiter
      mycursor.execute('SELECT name FROM sqlite_master where (name = ?)', [table])
      row = mycursor.fetchall()
      valid_table = False
      if row:
          print (table,"table name verified")
          valid_table = True
      else:
          print (table,"Table name not in database", db_name)
      
      if valid_table:
          try:
              mycursor.execute('insert into ' +use_delimiter+ table +use_delimiter+ ' (my_data,my_column_name) values (?,?) ',(1,"Name"));
              db.commit()
          except Exception as e:
              print "Error:", str(e)
          try:
              mycursor.execute('UPDATE ' +use_delimiter+ table +use_delimiter+ ' set my_column_name = ? where my_data = ?', ["ReNamed",1])
              db.commit()
          except Exception as e:
              print "Error:", str(e)
      db.close()
      

      【讨论】:

      • 如果我的表叫my table怎么办?这是完美的valid_table,但你的UPDATE 会失败。
      • @har-wradim 是的!你是绝对正确的。如果您决定使用包含空格的表名,就像 sqlite 中包含空格的字段名一样有效,但除非您想要一个痛苦的世界,否则您不会使用它们。
      • 我只是想强调您的解决方案没有解决问题。一个表可能存在于数据库中,同时具有一个不方便的名称。
      • 不,这不是解决方案。表格可以有任何名称(是的,它们可以包含引号、括号、与关键字相同并且类似于 Bobby-Table)- 空格只是一个示例。参见例如这个blog
      • @har-wradim 再次编辑,按照blog.christosoft.de/2012/10/sqlite-escaping-table-acolumn-names使用双引号
      【解决方案7】:

      你可以使用这样的东西 conn = sqlite3.connect() createTable = '''CREATE TABLE %s (# );''' %dateNow) conn.execute(createTable)

      基本上,如果我们现在想根据日期将数据分成几个表,例如,您想根据日期监控系统。

      createTable = '''CREATE TABLE %s (# );''' %dateNow) 表示您创建一个带有变量 dateNow 的表,根据您的编码语言,您可以将 dateNow 定义为一个变量来检索当前日期来自您的编码语言。

      【讨论】:

      • 感谢您提供此代码 sn-p,它可能会提供一些有限的即时帮助。 proper explanation 将通过展示为什么这是解决问题的好方法,并使其对有其他类似问题的未来读者更有用,从而大大提高其长期价值。请edit您的回答添加一些解释,包括您所做的假设。
      【解决方案8】:

      您可以将查询保存在 .sql 或 txt 文件中,并使用 open().replace() 方法在查询的任何部分使用变量。长期阅读,但第一次发帖,如果这里有任何问题,我深表歉意。

      ```SQL in yoursql.sql```
      Sel *
      From yourdbschema.tablenm
      
      
      ```SQL to run```
      tablenm = 'yourtablename'
      
      cur = connect.cursor() 
      
      query = cur.execute(open(file = yoursql.sql).read().replace('tablenm',tablenm))

      【讨论】:

        【解决方案9】:

        您可以将字符串作为 SQL 命令传递:

        import sqlite3
        conn = sqlite3.connect('db.db')
        c = conn.cursor()
        tablename, field_data = 'some_table','some_data'
        query = 'SELECT * FROM '+tablename+' WHERE column1=\"'+field_data+"\""
        c.execute(query)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2017-10-12
          • 2017-01-04
          • 2019-10-05
          • 1970-01-01
          • 2021-10-07
          • 1970-01-01
          相关资源
          最近更新 更多