Подтвердить что ты не робот

Выполнение "SELECT... WHERE... IN..." с использованием MySQLdb

У меня возникла проблема с выполнением некоторого SQL из Python, несмотря на то, что аналогичный SQL работает нормально из командной строки mysql.

Таблица выглядит так:

mysql> SELECT * FROM foo;
+-------+-----+
| fooid | bar |
+-------+-----+
|     1 | A   | 
|     2 | B   | 
|     3 | C   | 
|     4 | D   | 
+-------+-----+
4 rows in set (0.00 sec)

Я могу выполнить следующий SQL-запрос из командной строки mysql без проблем:

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, я могу получить код, чтобы выбрать те bar, где fooid находится в (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:

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, 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   [email protected] 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.

Благодаря комментарию @Amber, я лучше понимаю, что происходит не так. MySQLdb преобразует параметризованный аргумент ['A','C'] в ("'A'","'C'").

Есть ли способ сделать параметризованный запрос, используя синтаксис IN SQL? Или нужно вручную построить строку SQL?

4b9b3361

Ответ 1

Вот подобное решение, которое, по моему мнению, более эффективно в создании списка строк% s в SQL:

Используйте 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, как параметр (не в тексте запроса), поэтому нет инъекции. Вы можете оставить любые символы, которые вы хотите в строке, не нужно удалять или указывать символы.

Ответ 2

К сожалению, вам нужно вручную сконструировать параметры запроса, поскольку, насколько мне известно, нет встроенного метода bind для привязки list к предложению 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)

Ответ 3

Если у вас есть другие параметры в запросе, вне списка 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))

То есть, присоедините все параметры в линейном массиве, а затем передайте его как кортеж методу выполнения.

Ответ 4

это работает для меня:

myTuple= tuple(myList)
sql="select fooid from foo where bar in "+str(myTuple)
cursor.execute(sql)

Ответ 5

Может быть, мы можем создать функцию, чтобы сделать то, что предложил Жоау? Что-то вроде:

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)

Ответ 6

Пробовал все варианты решения João, чтобы получить запрос IN List для работы с оберткой Tornado mysql и по-прежнему получал проклятую ошибку "TypeError: not enough arguments for format string". Оказывается, добавление "*" в список var "* 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)

Ответ 7

Улучшая код João и satru, я предлагаю создать микшер курсора, который можно использовать для построения курсора с выполнением, который принимает вложенные итерации и корректно обрабатывает их. Лучшее имя было бы неплохо, хотя... Для 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

Затем это можно использовать следующим образом (и оно все еще обратное совместимо!):

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

Ответ 8

Очень просто:

Просто используйте следующую формулу ###

rules_id = ["9","10"]

sql2 = "SELECT * FROM attendance_rules_staff WHERE id in"+str(tuple(rules_id))

обратите внимание на str(tuple(rules_id)).

Ответ 9

Почему бы не просто в этом случае?

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

Ответ 10

args должен быть кортежем.

например:

args = ('A','B')

args = ('A',) # in case of single