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 ?

71voto

João Silva Points 36619

Malheureusement, vous devez construire manuellement les paramètres de la requête, car pour autant que je sache, il n'y a pas d'interface intégrée pour les paramètres de la requête. bind méthode pour lier un list à un IN similaire à celle d'Hibernate. setParameterList() . Cependant, vous pouvez accomplir la même chose avec ce qui suit :

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)

43voto

Robert Bergs Points 45

Voici un solution similaire ce qui, je pense, est plus efficace pour construire la liste des chaînes %s dans le SQL :

Utilisez le list_of_ids directement :

format_strings = ','.join(['%s'] * len(list_of_ids))
cursor.execute("DELETE FROM foo.bar WHERE baz IN (%s)" % format_strings,
                tuple(list_of_ids))

De cette façon, vous évitez d'avoir à vous citer vous-même, et vous évitez toutes sortes d'injection sql.

Notez que les données ( list_of_ids ) va directement au pilote de mysql, en tant que paramètre (pas dans le texte de la requête), donc il n'y a pas d'injection. Vous pouvez laisser tous les caractères que vous voulez dans la chaîne, il n'est pas nécessaire de les supprimer ou de les citer.

10voto

Si vous avez d'autres paramètres dans la requête, au-delà de la liste IN, alors l'extension suivante de la réponse de JG peut être utile.

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

Il s'agit de réunir tous les paramètres dans un tableau linéaire, puis de le transmettre sous forme de tuple à la méthode d'exécution.

6voto

shanal Points 6

Cela fonctionne pour moi :

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

2voto

Smooth Customer Points 53

J'ai essayé toutes les variantes de la solution de João pour faire fonctionner une requête IN List avec le wrapper mysql de Tornado, et j'obtenais toujours la maudite erreur "TypeError : not enough arguments for format string". Il s'avère que l'ajout de "*" à la liste var "*args" a fait l'affaire.

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)

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