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

时间:2018-02-06 22:36:53

标签: python json mysql-python string.format

我的目标是使用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}')的使用,从而避免使用{{1} (无论我的原因是什么,我们不要在这里进行辩论 - 但这是我希望尽可能保留它的原因,因此我的问题)。

在任何情况下,无论我尝试哪种方式,我都无法使用类似于上述结构的东西来创建将jsonData存储到mySql数据库中的东西,并使用%s(无论以何种形式或形式)代替'...{}'.format()。例如,我(在许多迭代中)尝试了

%s

但无论我如何扭转它,我都会遇到以下错误:

  

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

现在我的问题:

1)有没有办法避免在这里使用%s,我错过了?

2)如果没有,为什么?是什么让这变得不可能?它是insert_statement = "INSERT INTO table (id_field, json_data_field) VALUES ({}, {})".format(1, jsonData) cursor.execute(insert_statement) 函数,还是它是一个JSON对象,还是完全不同的东西? cursor.execute()不能做{}.format()可以做的所有事情吗?

2 个答案:

答案 0 :(得分:3)

首先:绝对不要将您的数据直接插入您的查询字符串中

在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)

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

效果

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

答案 1 :(得分: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以及在execute函数中使用(condition_value,)。