5

I mean something like this:

from django.db import connection
cursor=connection.cursor()
cursor.execute('SELECT * FROM mytable where id IN (%s)', [params])

Parameters can not be just iterable - it doesn't work. Cannot be also in CSV format due escaping value by db handler.

How to use placeholder within IN ?


By CSV is wrong, I mean that for params=['1','2','3','4','5']

c.execute('select * from mytable where id in (%s)', [','.join(params)])

will produce:

select * from mytable where id  in ('1,2,3,4,5')

but correct sql is:

select * from mytable where id  in (1,2,3,4,5)

and it seems to be hard to achieve with placeholders.

Sławomir Lenart
  • 7,543
  • 4
  • 45
  • 61
  • 1
    Not sure what you mean by 'it cannot be in CSV format'. IN expects a list of values, which necessarily must be separated by a comma. If your `params` are a list, you should do something like `','.join(params)`. That will convert a list like `[1, 2, 3]` into a string like `1,2,3`. – Björn Kristinsson Aug 18 '16 at 07:54
  • Be careful using `%s` as a placeholder in SQl, it can leave you vulnerable to an SQL injection https://xkcd.com/327/ – Chris_Rands Aug 18 '16 at 07:56
  • @Chris_Rands: it's not about dummy `"%s" % params`. – Sławomir Lenart Aug 18 '16 at 08:08
  • What version of SQL are you using? – Chris_Rands Aug 18 '16 at 08:12
  • why are you querying with a cursor if you are using django?? – e4c5 Aug 18 '16 at 09:34
  • @e4c5: .. because building complex and optimized and readable query in django ORM is a pain in ... . I have many cases in project which requires joining 10-30 mysql tables and using extra functions or aggregations. Much faster is achieve it in plain SQL than django, especially when you need have it optimized and readable (for debug purspose at least).. – Sławomir Lenart Oct 28 '16 at 09:46

3 Answers3

4

You cannot use IN for this since indeed, in expects a sequence of ints, but the ORM translates a list to an ARRAY, and if you use join you'll end up with a string.

The solution is to use the equivalent ANY. The formatting is slightly different, but this should work for you:

c.execute('select * from mytable where id = ANY(%s)', [params])

Given params = [1, 2, 3, 4, 5], the resulting SQL will be:

SELECT * FROM mytable where id = ANY(ARRAY[1, 2, 3, 4, 5])

Note that this requires the list of ids to be made up of ints, so if you have a list of strings be sure to convert them first.

2

Updated Answer Sorry about the older answer. This should work as you want, but might not be the best solution. I tried it with both, params as a list of strings and a list of integers.

from django.db import connection

params = [1, 2, 3, '4', '5']
placeholders = ('%s,'*len(params)).rstrip(',')  # Having a comma at the end will give a syntax error

with connection.cursor() as cursor:
    cursor.execute('SELECT * FROM mytable where id IN ({})'.format(placeholders), params)
    #  Use the cursor here 

End updated answer


In SQL the value for IN needs to be a comma separated list

SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1,value2,...);

So the simplest approach for you, assuming that params is an iterable would be

from django.db import connection
with connection.cursor() as cursor:
    cursor.execute('SELECT * FROM mytable where id IN (%s)', [', '.join(params)])  

The join will convert your iterable params into a comma separated string which will then replace the %s.

Generally you will want your query params to be escaped. Even the data you stored should be escaped.

Edit: Also notice that I have moved your cursor into a with block. You should always close your connections. Read up more here.

Resley Rodrigues
  • 2,218
  • 17
  • 17
-2

Solution is rather simple:

c.execute('select * from mytable where id in (%s)' % (','.join(params),))

See similar topic: imploding a list for use in a python MySQLDB IN clause

Community
  • 1
  • 1
Smooth Fire
  • 183
  • 5
  • yes, it is, but above don't use cursor placeholder - it's rather workaround. However, it's possible with MySql db handler using named parameters in formatting query, but unobtainable with django db handler. – Sławomir Lenart Aug 18 '16 at 10:29
  • 1
    Please be aware, this is ACTIVELY DANGEROUS. By using normal string formatting you are introducing an SQL Injection attack vector, especially if params are from any form of user left data. Do not, under any circumstances, use this code. – Darian Moody Feb 27 '18 at 15:46
  • Yikes, dont use string interpolation!!!!! This i the #1 way stuff gets hacked!!! – Shayne Jan 27 '20 at 01:51