我在从Python内部执行某些SQL时遇到了问题,尽管类似的SQL在mysql命令行中也能正常工作。
mysql
该表如下所示:
mysql> SELECT * FROM foo; +-------+-----+ | fooid | bar | +-------+-----+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | +-------+-----+ 4 rows in set (0.00 sec)
我可以从mysql命令行执行以下SQL查询,而不会出现问题:
mysql> SELECT fooid FROM foo WHERE bar IN ('A','C'); SELECT fooid FROM foo WHERE bar IN ('A','C'); +-------+ | fooid | +-------+ | 1 | | 3 | +-------+ 2 rows in set (0.00 sec)
但是,当我尝试在Python中执行相同操作时,没有任何行,而我希望有2行:
import MySQLdb import config connection=MySQLdb.connect( host=config.HOST,user=config.USER,passwd=config.PASS,db='test') cursor=connection.cursor() sql='SELECT fooid FROM foo WHERE bar IN %s' args=[['A','C']] cursor.execute(sql,args) data=cursor.fetchall() print(data) # ()
所以,问题是:应该如何Python代码进行修改,以选择那些fooidS其中bar是('A','C')?
fooid
bar
('A','C')
顺便说一句,我注意到,如果我切换的角色bar和fooid,我可以得到代码选择那些barS其中fooid是(1,3)成功的。我不明白为什么一个这样的查询(下面)有效,而另一个(上面)却无效。
(1,3)
sql='SELECT bar FROM foo WHERE fooid IN %s' args=[[1,3]] cursor.execute(sql,args) data=cursor.fetchall() print(data) # (('A',), ('C',))
绝对要清楚,这是foo表的创建方式:
foo
mysql> DROP TABLE IF EXISTS foo; Query OK, 0 rows affected (0.00 sec) mysql> CREATE TABLE `foo` ( `fooid` int(11) NOT NULL AUTO_INCREMENT, `bar` varchar(10) NOT NULL, PRIMARY KEY (`fooid`)); Query OK, 0 rows affected (0.01 sec) mysql> INSERT into foo (bar) values ('A'),('B'),('C'),('D'); Query OK, 4 rows affected (0.00 sec) Records: 4 Duplicates: 0 Warnings: 0
编辑 :当我启用通用查询日志,mysqld -l /tmp/myquery.log 我看到
mysqld -l /tmp/myquery.log
mysqld, Version: 5.1.37-1ubuntu5.5-log ((Ubuntu)). started with: Tcp port: 3306 Unix socket: /var/run/mysqld/mysqld.sock Time Id Command Argument 110101 11:45:41 1 Connect unutbu@localhost on test 1 Query set autocommit=0 1 Query SELECT fooid FROM foo WHERE bar IN ("'A'", "'C'") 1 Query SELECT bar FROM foo WHERE fooid IN ('1', '3') 1 Quit
实际上,似乎在A和周围放置了过多的引号C。
A
C
感谢@Amber的评论,我更好地了解出了什么问题。MySQLdb将参数化参数转换['A','C']为("'A'","'C'")。
['A','C']
("'A'","'C'")
有没有一种方法可以使用INSQL语法进行参数化查询?还是必须手动构造SQL字符串?
IN
这是一个类似的解决方案,我认为在SQL中建立%s字符串列表更有效:
list_of_ids直接使用: format_strings = ','.join(['%s'] * len(list_of_ids)) cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings, tuple(list_of_ids)) 这样,您就不必引用自己的报价,并避免各种SQL注入。 请注意,数据(list_of_ids)作为参数直接进入mysql的驱动程序(不在查询文本中),因此没有注入。您可以在字符串中保留所需的任何字符,而无需删除或引用字符。
list_of_ids直接使用:
list_of_ids
format_strings = ','.join(['%s'] * len(list_of_ids)) cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings, tuple(list_of_ids))
这样,您就不必引用自己的报价,并避免各种SQL注入。
请注意,数据(list_of_ids)作为参数直接进入mysql的驱动程序(不在查询文本中),因此没有注入。您可以在字符串中保留所需的任何字符,而无需删除或引用字符。