Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/69.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 执行;选择。。。哪里在……中”;使用MySQLdb_Python_Mysql - Fatal编程技术网

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