怎么了
在检查了下面注释中的链接并进行了更多的研究和测试之后,我能够使用MySQLdb版本1.2.4b4和1.2.5重现该错误。如unubtu的答案所述,这与出现在中的正则表达式的局限性有关
cursors.py。各个版本中的确切正则表达式略有不同,这可能是因为人们一直在寻找它无法处理的情况并调整表达式,而不是完全寻找更好的方法。
正则表达式的作用是尝试匹配语句的
VALUES ( ...)子句,
INSERT并标识其包含的元组表达式的开头和结尾。如果匹配成功,则
executemany尝试将单行插入语句模板转换为多行插入语句,以使其运行更快。即,不要对要插入的每一行执行此操作:
INSERT INTO table (foo, bar, ...)VALUES (%s, %s, ...);
它尝试重写该语句,以便只执行一次:
INSERT INTO table (foo, bar, ...)VALUES (1, 2, ...), (3, 4, ...), (5, 6, ...), ...;
您遇到的问题是,
executemany假设紧随在元组中只有参数占位符
VALUES。当您以后还有占位符时,它将执行以下操作:
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,则可以避免,
TypeError但会遇到其他问题。请注意,重写的
INSERT... ON DUPLICATEUPDATE查询在
VALUES子句中具有数字文字,但是子句中仍然有一个
%s占位符
UPDATE。当它到达MySQL服务器时,将引发语法错误。
当我第一次测试您的示例代码时,我使用的是MySQLdb
1.2.3c1,无法重现您的问题。有趣的是,特定版本的软件包避免了这些问题的原因是正则表达式已损坏并且根本与语句不匹配。由于不匹配,因此
executemany不会尝试重写查询,而只是循环遍历您的参数
execute重复调用。
该怎么办
首先,不要回去安装1.2.3c1来完成这项工作。您希望尽可能使用更新的代码。
您可以移至另一个软件包,如在链接的“问答”中未曾建议的那样,但这将涉及一些调整,并且可能需要更改其他代码。
我建议改用一种更直接的方式来重写查询,并利用子句中的
VALUES()功能
UPDATE。通过此函数,您 可以
按列名引用在没有重复键冲突的情况下要插入的值(示例在MySQL文档中)。
考虑到这一点,这是一种实现方法:
dData = [[u'Daniel', u'00-50-56-C0-00-12', u'Daniel']] # exact input you gavesql = """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 valuescur.executemany(sql, ((bsid, shop_id, dType, mac, rem) for mac, rem in dData))
这种方法应该起作用,因为该
UPDATE子句中没有参数,这意味着MySQLdb将能够成功地将带有参数的单行插入模板转换为具有文字值的多行插入语句。
注意事项:
- 您不必提供元组
executemany
;任何迭代都可以。 - 与隐式连接的字符串相比,多行字符串使Python代码中的SQL语句可读性更高。当您将语句与字符串定界符分开时,很容易快速抓取该语句并将其复制到客户端应用程序中进行测试。
- 如果要参数化部分查询,为什么不参数化所有查询?即使仅一部分是用户输入,以相同的方式处理所有输入值也更具可读性和可维护性。
- 就是说,我没有参数化
NOW()
。我在这里的首选方法是将它CURRENT_TIMESTAMP
用作列的默认值,并DEFAULT
在语句中加以利用。其他人可能更喜欢在应用程序中生成此值并将其作为参数提供。如果您不担心版本兼容性,则可以保持原样。 - 如果您无法避免在
UPDATE
子句中使用参数占位符(例如,由于UPDATE
不能在语句中对值进行硬编码或从VALUES
元组派生出值),则必须进行迭代execute
而不是使用executemany
。



