【问题标题】:Use of '.format()' vs. '%s' in cursor.execute() for mysql JSON field, with Python mysql.connector,在 mysql JSON 字段的 cursor.execute() 中使用“.format()”与“%s”,使用 Python mysql.connector,
【发布时间】:2018-02-06 22:36:53
【问题描述】:

我的目标是使用 mysql.connector 库将 JSON 对象存储到 json 类型的 MySQL 数据库字段中。

import mysql.connector
import json

jsonData = json.dumps(origin_of_jsonData)

cnx = mysql.connector.connect(**config_defined_elsewhere)
cursor = cnx.cursor()
cursor.execute('CREATE DATABASE dataBase')
cnx.database = 'dataBase'
cursor = cnx.cursor()
cursor.execute('CREATE TABLE table (id_field INT NOT NULL, json_data_field JSON NOT NULL, PRIMARY KEY (id_field))')

现在,下面的代码可以正常工作了,我的问题的重点是 '%s' 的使用:

insert_statement = "INSERT INTO table (id_field, json_data_field) VALUES (%s, %s)"
values_to_insert = (1, jsonData)
cursor.execute(insert_statement, values_to_insert)

我的问题是:在将变量 aValue(s) 组合成字符串时,我非常严格地坚持使用'...{}'.format(aValue)(或f'...{aValue}'),从而避免使用%s(无论我出于何种原因那个,我们不要在这里讨论它们 - 但这是我希望尽可能保留它的方式,因此我的问题)。

无论如何,无论我尝试哪种方式,我都无法使用类似于上述结构的东西创建将 jsonData 存储到 mySql 数据库中的东西,并使用'...{}'.format()(无论形状或形式)而不是@ 987654329@。例如,我(在许多迭代中)尝试过

insert_statement = "INSERT INTO table (id_field, json_data_field) VALUES ({}, {})".format(1, jsonData)
cursor.execute(insert_statement)

但是无论我怎么转和扭它,我都会收到以下错误:

ProgrammingError: 1064 (42000): 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册,以在第 1 行的“[some_content_from_jsonData})]”附近使用正确的语法

现在我的问题:

1) 有没有办法避免在这里使用我遗漏的 %s?

2) 如果不是,为什么?是什么让这成为不可能?是 cursor.execute() 函数,还是它是一个 JSON 对象,还是完全不同的东西? {}.format() 不应该能够做 %s 可以做的所有事情,甚至更多吗?

【问题讨论】:

  • 您可能有兴趣阅读Python best practice and securest to connect to MySQL and execute queries"some string %s" % "other string"(注意% 不是两个字符串之间的,)相当于"some string {}".format("other string"),你应该在查询中避免这两个
  • 你应该使用%s。这不是字符串替换的情况。它是 SQL 参数插入。它恰好使用了%s,所以看起来有点像% 运算符。
  • 您没有说明为什么要避免%s,但这样做是明显错误,并且会使您面临SQL 注入的风险。你应该使用它。
  • 您可以尝试使用字符串的__repr__,这将(大部分时间但不是所有时间)将字符串放在单引号中,因此请使用:"INSERT INTO table (id_field, json_data_field) VALUES ({}, {!r})".format(1, jsonData)然而,这个结果是脆弱的。最好使用内置的SQL参数插入,确实有各种原因。
  • 非常感谢您的回答!特别是解释(显式和隐式)这与字符串替换无关,但是恰好看起来像字符串替换的 SQL 参数插入是我正在寻找的信息......现在可能看起来是一个愚蠢的问题,但如果你刚刚开始,这些都是出现的问题类型 :) 再次感谢您!

标签: python json mysql-python string.format


【解决方案1】:

首先:永远不要将您的数据直接插入到您的查询字符串中!

在 MySQL 查询字符串中使用 %s 与在 python 字符串中使用它不同。 在 python 中,你只需格式化字符串,'hello %s!' % 'world' 就变成了'hello world!'。在 SQL 中,%s 表示参数插入。这会将您的查询和数据分别发送到服务器。您也不受此语法的约束。 python DB-API 规范为此指定了更多样式:DB-API parameter styles (PEP 249)。与将数据直接插入查询字符串相比,这有几个优点:

防止 SQL 注入

假设您有一个通过密码对用户进行身份验证的查询。您可以使用以下查询来做到这一点(当然您通常会对密码进行加盐和哈希处理,但这不是本问题的主题):

SELECT 1 FROM users WHERE username='foo' AND password='bar'

构造这个查询的简单方法是:

"SELECT 1 FROM users WHERE username='{}' AND password='{}'".format(username, password)

但是,如果有人输入' OR 1=1 作为密码会发生什么。格式化后的查询将变为

SELECT 1 FROM users WHERE username='foo' AND password='' OR 1=1

总是返回1。使用参数插入时:

execute('SELECT 1 FROM users WHERE username=%s AND password=%s', username, password)

这永远不会发生,因为查询将由服务器单独解释。

性能

如果您使用不同的数据多次运行相同的查询,则使用格式化查询和参数插入之间的性能差异可能会很大。使用参数插入,服务器只需编译一次查询(因为每次都一样)并使用不同的数据执行它,但使用字符串格式,它必须一遍又一遍地编译它。

【讨论】:

  • 嗨 Jesse - 非常感谢你,特别是还添加了有关性能的信息(这对我来说非常相关)!
  • @Mephisto3 用于插入多行,看看executemany 函数
  • 再次感谢您!还要感谢您指出我将答案标记为已接受...显然,我对很多事情都很陌生,其中之一是 Stackoverflow(至少作为发布问题的人)...
【解决方案2】:

除了上面所说的之外,我想补充一些我没有立即理解的细节,其他(像我这样的新手;))也可能会有所帮助:

1) “参数插入”仅用于值,它不适用于表名、列名等 - 对于这些,Python 字符串替换在 sql 语法中可以正常工作定义

2) cursor.execute 函数需要一个元组 才能工作(在这里指定,虽然不是立即清楚,至少对我来说:https://dev.mysql.com/doc/connector-python/en/connector-python-api-mysqlcursor-execute.html

一个功能的例子:

def checkIfRecordExists(column, table, condition_name, condition_value):
    ...
    sqlSyntax = 'SELECT {} FROM {} WHERE {} = %s'.format(column, table, condition_name)
    cursor.execute(sqlSyntax, (condition_value,))

注意在初始 sql 语法定义中使用 .format 和在执行函数中使用 (condition_value,)。

【讨论】:

    猜你喜欢
    • 2020-06-13
    • 2019-05-10
    • 1970-01-01
    • 2017-03-11
    • 1970-01-01
    • 2017-05-28
    • 2019-10-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多