【问题标题】:MySQL parameterized queriesMySQL参数化查询
【发布时间】:2021-09-29 01:04:48
【问题描述】:

我很难使用 MySQLdb 模块将信息插入我的数据库。我需要在表中插入 6 个变量。

cursor.execute ("""
    INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
    VALUES
        (var1, var2, var3, var4, var5, var6)

""")

有人可以帮我看看这里的语法吗?

【问题讨论】:

    标签: python mysql bind-variables


    【解决方案1】:

    注意对 SQL 查询使用字符串插值,因为它不会正确转义输入参数,并且会使您的应用程序容易受到 SQL 注入漏洞的影响。 差异看似微不足道,但实际上却很大

    不正确(存在安全问题)

    c.execute("SELECT * FROM foo WHERE bar = %s AND baz = %s" % (param1, param2))
    

    正确(带转义)

    c.execute("SELECT * FROM foo WHERE bar = %s AND baz = %s", (param1, param2))
    

    这增加了混淆,用于在 SQL 语句中绑定参数的修饰符在不同的 DB API 实现之间有所不同,并且 mysql 客户端库使用printf 样式语法而不是更普遍接受的“?”标记(由例如python-sqlite 使用)。

    【讨论】:

    • @Specto IMO 始终坚持正确和安全的实施方式是有意义的。它创造了正确的习惯和良好的编程文化。也没有人知道您的代码将来将如何使用;以后有人可以将它用于其他系统或网站。
    • @BryanHunt 你可以打开使用?在某处有一个论点,但不鼓励这样做,因为它并没有告诉你很多关于哪个论点在哪里。 (当然,%s 也可以这样说,出于同样的原因不鼓励这样做。)更多信息:python.org/dev/peps/pep-0249/#paramstyle
    • 来自 php/pdo,如果 printf 样式 %s 标记,我感到非常困惑,而且我有点害怕我正在编写易受攻击的查询。感谢您消除了这个担忧! :)
    • 单参数时记得保留逗号:c.execute("SELECT * FROM foo WHERE bar = %s", (param1,))
    • 关于游标执行中参数的上下文(以及为什么仍然需要 %s),MySQLdb 游标的 api 参考位于 mysql-python.sourceforge.net/MySQLdb-1.2.2/public/…
    【解决方案2】:

    您有几个可用的选项。你会想要熟悉 python 的字符串迭代。当您想了解此类内容时,您将来可能会更成功地搜索哪个术语。

    更适合查询:

    some_dictionary_with_the_data = {
        'name': 'awesome song',
        'artist': 'some band',
        etc...
    }
    cursor.execute ("""
                INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
                VALUES
                    (%(name)s, %(artist)s, %(album)s, %(genre)s, %(length)s, %(location)s)
    
            """, some_dictionary_with_the_data)
    

    考虑到您可能已经将所有数据保存在一个对象或字典中,第二种格式更适合您。当您必须在一年内回来并更新此方法时,必须在字符串中计算“%s”的出现次数也很糟糕:)

    【讨论】:

    • 看起来字典方法在 SQL 语句中的多个位置必须使用给定绑定变量的情况下效果更好。使用位置方法,我们需要根据引用的次数传递变量,这不是很理想。
    【解决方案3】:

    链接的文档给出了以下示例:

       cursor.execute ("""
             UPDATE animal SET name = %s
             WHERE name = %s
           """, ("snake", "turtle"))
       print "Number of rows updated: %d" % cursor.rowcount
    

    因此,您只需将其调整为您自己的代码 - 示例:

    cursor.execute ("""
                INSERT INTO Songs (SongName, SongArtist, SongAlbum, SongGenre, SongLength, SongLocation)
                VALUES
                    (%s, %s, %s, %s, %s, %s)
    
            """, (var1, var2, var3, var4, var5, var6))
    

    (如果 SongLength 是数字,您可能需要使用 %d 而不是 %s)。

    【讨论】:

    • 在 var1 和 var2 具有 " 或 ' 等字符的情况下是否可以使用。
    • AFAIK 无论哪种类型,您都必须在那里使用%s。 @sheki:是的
    【解决方案4】:

    实际上,即使您的变量 (SongLength) 是数字,您仍然需要使用 %s 对其进行格式化才能正确绑定参数。如果你尝试使用 %d,你会得到一个错误。这是此链接http://mysql-python.sourceforge.net/MySQLdb.html的一小段摘录:

    要执行查询,首先需要一个游标,然后可以在其上执行查询:

    c=db.cursor()
    max_price=5
    c.execute("""SELECT spam, eggs, sausage FROM breakfast
              WHERE price < %s""", (max_price,))
    

    在本例中,max_price=5 那么为什么在字符串中使用 %s?因为 MySQLdb 会将其转换为 SQL 文字值,即字符串 '5'。完成后,查询实际上会说“...WHERE price

    【讨论】:

    • 是的,这很奇怪......你认为“printf”格式意味着......实际上是 printf 格式,而不仅仅是在任何地方使用 %s。
    【解决方案5】:

    作为所选答案的替代方案,并且具有与 Marcel 相同的安全语义,这是一种使用 Python 字典指定值的紧凑方法。它的好处是在您添加或删除要插入的列时易于修改:

      meta_cols = ('SongName','SongArtist','SongAlbum','SongGenre')
      insert = 'insert into Songs ({0}) values ({1})'.format(
          ','.join(meta_cols), ','.join( ['%s']*len(meta_cols)))
      args = [ meta[i] for i in meta_cols ]
      cursor = db.cursor()
      cursor.execute(insert,args)
      db.commit()
    

    meta 是保存要插入的值的字典。更新可以用同样的方式完成:

      meta_cols = ('SongName','SongArtist','SongAlbum','SongGenre')
      update='update Songs set {0} where id=%s'.
            .format(','.join([ '{0}=%s'.format(c) for c in meta_cols ]))
      args = [ meta[i] for i in meta_cols ]
      args.append(songid)
      cursor=db.cursor()
      cursor.execute(update,args)
      db.commit()
    

    【讨论】:

    • 我印象深刻......你设法让 python 代码不可读!
    【解决方案6】:

    第一个解决方案效果很好。我想在这里添加一个小细节。确保您尝试替换/更新的变量必须是 str 类型。我的 mysql 类型是十进制,但我必须将参数变量设置为 str 才能执行查询。

    temp = "100"
    myCursor.execute("UPDATE testDB.UPS SET netAmount = %s WHERE auditSysNum = '42452'",(temp,))
    myCursor.execute(var)
    

    【讨论】:

      【解决方案7】:

      这是另一种方法。它记录在 MySQL 官方网站上。 https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html

      在精神上,它使用了与@Trey Stout 的回答相同的机制。不过,我觉得这个更漂亮,更易读。

      insert_stmt = (
        "INSERT INTO employees (emp_no, first_name, last_name, hire_date) "
        "VALUES (%s, %s, %s, %s)"
      )
      data = (2, 'Jane', 'Doe', datetime.date(2012, 3, 23))
      cursor.execute(insert_stmt, data)
      

      为了更好地说明对变量的任何需求:

      注意:请注意正在完成的转义。

      employee_id = 2
      first_name = "Jane"
      last_name = "Doe"
      
      insert_stmt = (
        "INSERT INTO employees (emp_no, first_name, last_name, hire_date) "
        "VALUES (%s, %s, %s, %s)"
      )
      data = (employee_id, conn.escape_string(first_name), conn.escape_string(last_name), datetime.date(2012, 3, 23))
      cursor.execute(insert_stmt, data)
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-09-15
        • 2019-09-05
        • 2011-12-13
        相关资源
        最近更新 更多