53 votes

Exécution de "SELECT ... WHERE ... IN ..." en utilisant MySQLdb

J'ai un problème d'exécution de certains SQL à partir de Python, alors que des SQL similaires fonctionnent bien à partir de l'interface utilisateur. mysql en ligne de commande.

Le tableau se présente comme suit :

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

Je peux exécuter la requête SQL suivante à partir de la ligne de commande mysql, sans problème :

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)

Cependant, lorsque j'essaie de faire la même chose à partir de Python, je n'obtiens aucune ligne, alors que je m'attendais à 2 lignes :

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

La question est donc la suivante : comment modifier le code python pour sélectionner ces fooidbar est en ('A','C') ?

Au fait, j'ai remarqué que si j'échange les rôles de bar y fooid je peux obtenir le code pour sélectionner ceux barfooid est en (1,3) avec succès. Je ne comprends pas pourquoi une de ces requêtes (ci-dessous) fonctionne, alors que l'autre (ci-dessus) ne fonctionne pas.

sql='SELECT bar FROM foo WHERE fooid IN %s'
args=[[1,3]]
cursor.execute(sql,args)
data=cursor.fetchall()
print(data)
# (('A',), ('C',))

Et juste pour être absolument clair, c'est comme ça que la foo a été créé :

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

Modifier : Lorsque j'active le journal général des requêtes avec mysqld -l /tmp/myquery.log Je vois.

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

En effet, on dirait que trop de guillemets sont placés autour de A y C .

Grâce au commentaire de @Amber, je comprends mieux ce qui ne va pas. MySQLdb convertit l'argument paramétré ['A','C'] a ("'A'","'C'") .

Existe-t-il un moyen d'effectuer une requête paramétrée en utilisant la fonction IN Syntaxe SQL ? Ou faut-il construire manuellement la chaîne SQL ?

2voto

Luci Stanescu Points 1

En améliorant le code de João et de satru, je suggère de créer un mixin curseur qui peut être utilisé pour construire un curseur avec un exécuteur qui accepte les itérables imbriqués et les gère correctement. Un meilleur nom serait bienvenu, cependant... Pour Python3, utilisez str au lieu de 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

On peut alors l'utiliser comme suit (et c'est toujours rétrocompatible !) :

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

2voto

Mizanur Rahman Points 15

Très simple :

Il suffit d'utiliser la formation ci-dessous####

rules_id = ["9","10"]

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

noter le str(tuple(rules_id)) .

2voto

satru Points 11

Peut-être pouvons-nous créer une fonction pour faire ce que João a proposé ? Quelque chose comme :

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)

0voto

michael Points 11

Pourquoi ne pas se contenter de cela dans ce cas ?

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)

résulte en :

SELECT fooid FROM foo WHERE bar IN ('A', 'C')

-1voto

aman2357 Points 29

Les args doivent être des tuple.

eg :

args = ('A','B')

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X