Python 执行;选择。。。哪里在……中”;使用MySQLdb
尽管在Python 执行;选择。。。哪里在……中”;使用MySQLdb,python,mysql,Python,Mysql,尽管在mysql命令行中类似的SQL工作得很好,但我在Python中执行一些SQL时遇到了问题 该表如下所示: mysql> SELECT * FROM foo; +-------+-----+ | fooid | bar | +-------+-----+ | 1 | A | | 2 | B | | 3 | C | | 4 | D | +-------+-----+ 4 rows in set (0.00 sec) 我可以从mys
mysql
命令行中类似的SQL工作得很好,但我在Python中执行一些SQL时遇到了问题
该表如下所示:
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代码以选择fooid
中bar
所在的('A','C')
顺便说一句,我注意到如果我切换bar
和fooid
的角色,我可以让代码选择fooid
位于(1,3)
中的那些bar
s。我不明白为什么一个这样的查询(下面)有效,而另一个(上面)无效
为了明确起见,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启用常规查询日志时
我明白了
事实上,似乎在A
和C
周围加了太多引号
多亏了@Amber的评论,我更好地理解了哪里出了问题。MySQLdb将参数化参数['A','C']
转换为(“'A'”,'C')
有没有办法使用SQL语法中的
进行参数化查询?还是必须手动构造SQL字符串?不幸的是,您需要手动构造查询参数,因为据我所知,没有内置的bind
方法将列表
绑定到in
子句中的,类似于Hibernate的setParameterList()
。但是,您可以通过以下操作完成相同的操作:
Python 3:
args=['A', 'C']
sql='SELECT fooid FROM foo WHERE bar IN (%s)'
in_p=', '.join(list(map(lambda x: '%s', args)))
sql = sql % in_p
cursor.execute(sql, args)
Python 2:
args=['A', 'C']
sql='SELECT fooid FROM foo WHERE bar IN (%s)'
in_p=', '.join(map(lambda x: '%s', args))
sql = sql % in_p
cursor.execute(sql, args)
为什么不在这种情况下就这样呢
args = ['A', 'C']
sql = 'SELECT fooid FROM foo WHERE bar IN (%s)'
in_p =', '.join(list(map(lambda arg: "'%s'" % arg, args)))
sql = sql % in_p
cursor.execute(sql)
结果:
SELECT fooid FROM foo WHERE bar IN ('A', 'C')
如果查询中除了in列表之外还有其他参数,那么下面对JG答案的扩展可能会很有用
ids = [1, 5, 7, 213]
sql = "select * from person where type=%s and id in (%s)"
in_ids = ', '.join(map(lambda x: '%s', ids))
sql = sql % ('%s', in_ids)
params = []
params.append(type)
params.extend(ids)
cursor.execute(sql, tuple(params))
也就是说,将所有参数连接到一个线性数组中,然后将其作为元组传递给execute方法。也许我们可以创建一个函数来实现João提出的功能?比如:
def cursor_exec(cursor, query, params):
expansion_params= []
real_params = []
for p in params:
if isinstance(p, (tuple, list)):
real_params.extend(p)
expansion_params.append( ("%s,"*len(p))[:-1] )
else:
real_params.append(p)
expansion_params.append("%s")
real_query = query % expansion_params
cursor.execute(real_query, real_params)
以下是我认为在SQL中建立%s字符串列表更有效的方法:
直接使用ID列表
:
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\u of_id
)作为参数(不在查询文本中)直接发送到mysql的驱动程序,因此没有注入。您可以在字符串中保留所需的任何字符,无需删除或引用字符
我们一直在尝试João解决方案的每一种变体,以使列表内查询与Tornado的mysql包装器一起工作,但仍然遇到了令人讨厌的“TypeError:format string参数不足”错误。事实证明,将“*”添加到列表变量“*args”中就成功了
args=['A', 'C']
sql='SELECT fooid FROM foo WHERE bar IN (%s)'
in_p=', '.join(list(map(lambda x: '%s', args)))
sql = sql % in_p
db.query(sql, *args)
在改进了João和satru的代码之后,我建议创建一个游标mixin,该游标mixin可用于构建一个带有接受嵌套iterables并正确处理它们的execute的游标。一个更好的名字会更好,虽然。。。对于Python3,使用str
而不是basestring
from MySQLdb.cursors import Cursor
class BetterExecuteMixin(object):
"""
This mixin class provides an implementation of the execute method
that properly handles sequence arguments for use with IN tests.
Examples:
execute('SELECT * FROM foo WHERE id IN (%s) AND type=%s', ([1,2,3], 'bar'))
# Notice that when the sequence is the only argument, you still need
# a surrounding tuple:
execute('SELECT * FROM foo WHERE id IN (%s)', ([1,2,3],))
"""
def execute(self, query, args=None):
if args is not None:
try:
iter(args)
except TypeError:
args = (args,)
else:
if isinstance(args, basestring):
args = (args,)
real_params = []
placeholders = []
for arg in args:
# sequences that we treat as a single argument
if isinstance(arg, basestring):
real_params.append(arg)
placeholders.append('%s')
continue
try:
real_params.extend(arg)
placeholders.append(','.join(['%s']*len(arg)))
except TypeError:
real_params.append(arg)
placeholders.append('%s')
args = real_params
query = query % tuple(placeholders)
return super(BetterExecuteMixin, self).execute(query, args)
class BetterCursor(BetterExecuteMixin, Cursor):
pass
然后可以按如下方式使用它(它仍然向后兼容!):
这对我很有用:
myTuple= tuple(myList)
sql="select fooid from foo where bar in "+str(myTuple)
cursor.execute(sql)
args应该是tuple
例如:
非常简单:
只需使用下面的格式###
注意str(tuple(rules_id))
您可以查看MySQL服务器的日志并找出实际运行的查询吗?这似乎与字符串/varchar的解释方式有关(因为python int->mysql int工作得很好)。我想我回答了我自己的问题:如果单独传递参数,Mysqldb lib将正确转义它们。永远不要在SQL查询中直接传递参数!这导致SQL注入漏洞,这是一个主要的安全问题。参数应始终在参数元组中传递,以便SQL绑定库正确引用它们。不同的引擎有不同的引用规则,引用参数的唯一安全方法是让较低级别的库进行引用。in_p=','.join(itertools.repeat('%s',len(args))
这可能会导致sql注入。我们能做些更安全的事吗?@Sohaib这怎么会容易注射?用于查询的数据(args)与sql文本分开传递。字符串操作只添加了更多占位符“%s”,而不是易受攻击的实际数据。in_p=','.join(['%s']*len(args))
,当我们处于此状态时……如果存在SQL InyAction漏洞,则在SQL secuence中不会转义参数。exec使用查询字符串和参数时,参数会转义,但使用手动连接时不会转义。示例:在参数中添加双引号、反斜杠、换行符、通配符、退格等,字符集问题,例如,在utf8中定义连接,但在日本文本编码中转义查询,等等。有多少人在违反他们的发展的情况下实施了这个解决方案?我喜欢这个,我甚至会说,公认的答案是有害的!同意-这是一个更好的解决方案。上述情况会使您面临SQL注入攻击,例如,如果用户键入);从用户中选择用户名、密码、信用卡代码>他们的代码可以在数据库上运行。永远不要在SQL查询中直接传递参数!这导致SQL注入漏洞,这是一个主要的安全问题。参数应始终在参数元组中传递,以便SQL绑定库正确引用它们。不同的引擎有不同的引用规则,引用参数的唯一安全方法是让较低级别的库进行引用。只要rules\u id
不来自外部源,这是一个优雅的解决方案。如果
from MySQLdb.cursors import Cursor
class BetterExecuteMixin(object):
"""
This mixin class provides an implementation of the execute method
that properly handles sequence arguments for use with IN tests.
Examples:
execute('SELECT * FROM foo WHERE id IN (%s) AND type=%s', ([1,2,3], 'bar'))
# Notice that when the sequence is the only argument, you still need
# a surrounding tuple:
execute('SELECT * FROM foo WHERE id IN (%s)', ([1,2,3],))
"""
def execute(self, query, args=None):
if args is not None:
try:
iter(args)
except TypeError:
args = (args,)
else:
if isinstance(args, basestring):
args = (args,)
real_params = []
placeholders = []
for arg in args:
# sequences that we treat as a single argument
if isinstance(arg, basestring):
real_params.append(arg)
placeholders.append('%s')
continue
try:
real_params.extend(arg)
placeholders.append(','.join(['%s']*len(arg)))
except TypeError:
real_params.append(arg)
placeholders.append('%s')
args = real_params
query = query % tuple(placeholders)
return super(BetterExecuteMixin, self).execute(query, args)
class BetterCursor(BetterExecuteMixin, Cursor):
pass
import MySQLdb
conn = MySQLdb.connect(user='user', passwd='pass', db='dbname', host='host',
cursorclass=BetterCursor)
cursor = conn.cursor()
cursor.execute('SELECT * FROM foo WHERE id IN (%s) AND type=%s', ([1,2,3], 'bar'))
cursor.execute('SELECT * FROM foo WHERE id IN (%s)', ([1,2,3],))
cursor.execute('SELECT * FROM foo WHERE type IN (%s)', (['bar', 'moo'],))
cursor.execute('SELECT * FROM foo WHERE type=%s', 'bar')
cursor.execute('SELECT * FROM foo WHERE type=%s', ('bar',))
myTuple= tuple(myList)
sql="select fooid from foo where bar in "+str(myTuple)
cursor.execute(sql)
args = ('A','B')
args = ('A',) # in case of single
rules_id = ["9","10"]
sql2 = "SELECT * FROM attendance_rules_staff WHERE id in"+str(tuple(rules_id))