使用 '.format()' 与 '%s' 在 cursor.execute() 中用于 mysql JSON 字段,使用 Python mysql.connector,

4 python string.format json mysql-python

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

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))')
Run Code Online (Sandbox Code Playgroud)

现在,下面的代码可以正常工作,我的问题的重点是“%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)
Run Code Online (Sandbox Code Playgroud)

我的问题是:在将变量 aValue(s) 组合成一个字符串时,我非常严格地坚持使用'...{}'.format(aValue)(or f'...{aValue}'),从而避免使用%s(不管我的原因是什么,我们不要在这里辩论它们 - 但它是如何我想尽可能保留它,因此我的问题)。

在任何情况下,无论我尝试哪种方式,我都无法使用类似于上述结构并使用'...{}'.format()(以任何形状或形式)而不是%s. 例如,我(在多次迭代中)尝试过

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

但无论我如何转动和扭曲它,我都会收到以下错误:

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

现在我的问题:

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

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

Jes*_*ker 6

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

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

防止 SQL 注入

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

SELECT 1 FROM users WHERE username='foo' AND password='bar'
Run Code Online (Sandbox Code Playgroud)

构造此查询的天真方法是:

"SELECT 1 FROM users WHERE username='{}' AND password='{}'".format(username, password)
Run Code Online (Sandbox Code Playgroud)

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

SELECT 1 FROM users WHERE username='foo' AND password='' OR 1=1
Run Code Online (Sandbox Code Playgroud)

这将始终返回 1。使用参数插入时:

execute('SELECT 1 FROM users WHERE username=%s AND password=%s', username, password)
Run Code Online (Sandbox Code Playgroud)

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

表现

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


小智 5

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

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,))
Run Code Online (Sandbox Code Playgroud)

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