Python MySQLdb并不是返回所有使用“转换”的参数;“关于重复密钥更新”;
使用python中的MySQLdb包,我希望插入带有检查一些唯一键的记录。我使用的方法是executemany。参数是sql语句和元组。但当我执行它时,它出现了一个错误,上面写着“并非所有参数都已转换”。 代码如下:Python MySQLdb并不是返回所有使用“转换”的参数;“关于重复密钥更新”;,python,mysql-python,Python,Mysql Python,使用python中的MySQLdb包,我希望插入带有检查一些唯一键的记录。我使用的方法是executemany。参数是sql语句和元组。但当我执行它时,它出现了一个错误,上面写着“并非所有参数都已转换”。 代码如下: dData = [[u'Daniel', u'00-50-56-C0-00-12', u'Daniel']] sql = "INSERT INTO app_network_white_black_list (biz_id, shop_id, type, mac_phone, rem
dData = [[u'Daniel', u'00-50-56-C0-00-12', u'Daniel']]
sql = "INSERT INTO app_network_white_black_list (biz_id, shop_id, type, mac_phone, remarks, create_time) " \
"VALUES ({bsid}, {shop_id}, {type}, %s, %s, NOW()) " \
"ON DUPLICATE KEY UPDATE type={type}, remarks=%s, create_time=NOW()".format(bsid=bsid, shop_id=shop_id, type=dType)
cur.executemany(sql, tuple(dData))
有人说这是一只虫子。但是他们没有给我一条路可以跳过它。如果这是一个bug,请提供一个方法。您在
dData
中有三个元素,但只有两个%s
占位符可供它们进入。发生了什么问题
在检查了链接并进行了更多的研究和测试之后,我能够用MySQLdb版本1.2.4b4和1.2.5重现错误。如中所述,这与出现在cursors.py
中的正则表达式的限制有关。确切的正则表达式在每个版本中都略有不同,这可能是因为人们一直在寻找它不能处理的情况,并调整表达式,而不是完全寻找更好的方法
正则表达式所做的是尝试匹配INSERT
语句的VALUES(…)
子句,并标识它包含的元组表达式的开头和结尾。如果匹配成功,executemany
将尝试将单行insert语句模板转换为多行insert语句,以便运行更快。即,不是对要插入的每一行执行此操作:
INSERT INTO table
(foo, bar, ...)
VALUES
(%s, %s, ...);
它尝试重写语句,以便只执行一次:
INSERT INTO table
(foo, bar, ...)
VALUES
(1, 2, ...),
(3, 4, ...),
(5, 6, ...),
...;
您遇到的问题是,executemany
假定元组中只有紧跟在值之后的参数占位符。当您以后也有占位符时,将采用以下方式:
INSERT INTO table
(foo, bar, ...)
VALUES
(%s, %s, ...)
ON DUPLICATE KEY UPDATE baz=%s;
并试图这样重写它:
INSERT INTO table
(foo, bar, ...)
VALUES
(1, 2, ...),
(3, 4, ...),
(5, 6, ...),
...
ON DUPLICATE KEY UPDATE baz=%s;
这里的问题是MySQLdb试图在重写查询的同时进行字符串格式化。只有VALUES(…)
子句需要重写,因此MySQLdb尝试将所有参数放入匹配组(%s,%s…)
,而没有意识到某些参数需要进入UPDATE
子句
如果只将VALUES
子句的参数发送到executemany
,则可以避免类型错误
,但会遇到另一个问题。请注意,重写的INSERT。。。重复更新时
查询在值
子句中有数字文本,但在更新
子句中仍有一个%s
占位符。当它到达MySQL服务器时,将抛出一个语法错误
当我第一次测试您的示例代码时,我使用的是MySQLDB1.2.3c1,无法重现您的问题。有趣的是,包的特定版本避免这些问题的原因是正则表达式被破坏了,并且与语句根本不匹配。由于它不匹配,executemany
不会尝试重写查询,而是重复调用execute
来遍历参数
怎么办
首先,不要回去安装1.2.3c1来实现这一点。您希望尽可能使用更新的代码
正如unubtu在链接的Q&A中所建议的那样,您可以转移到另一个包,但这将涉及一些调整,并可能对其他代码进行更改
相反,我建议您以更直接的方式重写查询,并利用UPDATE
子句中的VALUES()
函数。此函数允许您按列名(示例在中)返回在没有重复键冲突的情况下插入的值
考虑到这一点,这里有一种方法:
dData = [[u'Daniel', u'00-50-56-C0-00-12', u'Daniel']] # exact input you gave
sql = """
INSERT INTO app_network_white_black_list
(biz_id, shop_id, type, mac_phone, remarks, create_time)
VALUES
(%s, %s, %s, %s, %s, NOW())
ON DUPLICATE KEY UPDATE
type=VALUES(type), remarks=VALUES(remarks), create_time=VALUES(create_time);
""" # keep parameters in one part of the statement
# generator expression takes care of the repeated values
cur.executemany(sql, ((bsid, shop_id, dType, mac, rem) for mac, rem in dData))
这种方法应该可以工作,因为UPDATE
子句中没有参数,这意味着MySQLdb将能够成功地将带有参数的单行insert模板转换为带有文本值的多行insert语句
需要注意的一些事项:
- 您不必向
executemany
提供元组;随便什么都行
- 多行字符串使Python代码中的SQL语句比隐式连接的字符串更具可读性;当您将语句与字符串分隔符分开时,很容易快速获取语句并将其复制到客户端应用程序中进行测试
- 如果要参数化部分查询,为什么不参数化所有查询?即使其中只有一部分是用户输入,以相同的方式处理所有输入值也更具可读性和可维护性
- 也就是说,我没有参数化
NOW()
。这里我首选的方法是使用CURRENT\u TIMESTAMP
作为列默认值,并利用语句中的default
。其他人可能更愿意在应用程序中生成此值并将其作为参数提供。如果你不担心版本的兼容性,它可能是好的
- 如果无法避免在
UPDATE
子句中使用参数占位符–例如,因为UPDATE
值不能在语句中硬编码或从VALUES
元组派生–则必须迭代executemany
,而不是使用executemany
sql中的第三行还有%s。您的回答非常有用。我会检查我的源代码是否有bug。但在另一个问题中,他们发现这是由MySQLdb中的常规express错误引起的。下面的链接可能更清楚:@HualiangLi谢谢你的链接,这很有帮助。事实上,你是对的,它与正则表达式有关;有关更多详细信息,请参阅我的更新答案。(答案基本上是一样的。)回答得真好!谢谢你的解决方案!很好的解释,