9

I am trying to create a statement as follows:

SELECT * FROM table WHERE provider IN ('provider1', 'provider2', ...)

However, I'm having some trouble with the string formatting of it from the Django API. Here's what I have so far:

profile = request.user.get_profile()
providers = profile.provider.values_list('provider', flat=True) # [u'provider1', u'provider2']
providers = tuple[str(item) for item in providers] # ('provider1', 'provider2')

SQL = "SELECT * FROM table WHERE provider IN %s"
args = (providers,)
cursor.execute(sql,args)

DatabaseError
(1241, 'Operand should contain 1 column(s)')
7
  • 4
    Curious. Why are you doing a raw sql IN query when you already have the django ORM? Commented Jun 25, 2012 at 19:29
  • @jdi It's a long sql query that I'm building with string concatenation based on some user-inputted values (about 20 lines long). Commented Jun 25, 2012 at 19:34
  • The ORM has aggregations though. But I guess I just have to take your word for it that the ORM can't do it :-) Commented Jun 25, 2012 at 19:53
  • I'm with @jdi here - is the table you're querying inside the ORM? (slight edit: I'm not with jdi, I mean that in the sense of a somewhat confused view...) Commented Jun 25, 2012 at 20:00
  • 1
    @JonClements: Jon, I am offended. We talked about being honest in public. David, we are together, and we also share similar opinions on database ORMs Commented Jun 25, 2012 at 20:14

6 Answers 6

6

MySQLdb has a method to help with this:

Doc

string_literal(...) string_literal(obj) -- converts object obj into a SQL string literal. This means, any special SQL characters are escaped, and it is enclosed within single quotes. In other words, it performs:

"'%s'" % escape_string(str(obj))

Use connection.string_literal(obj), if you use it at all.
_mysql.string_literal(obj) cannot handle character sets.

Usage

# connection:  <_mysql.connection open to 'localhost' at 1008b2420>

str_value = connection.string_literal(tuple(provider))
# '(\'provider1\', \'provider2\')'

SQL = "SELECT * FROM table WHERE provider IN %s"
args = (str_value,)
cursor.execute(sql,args) 
Sign up to request clarification or add additional context in comments.

5 Comments

thats pretty cool. Did not know about the string_literal method. I am assuming that since MySQLdb follows the db api pretty strictly, this exists in most implementations?(sqlite3, postgres, etc) Definetly going back to the docs
@Justin.Wood: To be honest, I didn't really know about it either, but I don't have much previous experience using MySQLdb and passing tuple values. Its always an ORM. I just looked at the docs too for this one :-)
Neat. I work in some legacy code base that doesnt use an ORM, and we get this kinda stuff constantly. So this is going to be a big plus for me. You got a link to this in the docs? Couldnt find it. Though it is easy enough to find in the source.
@Justin.Wood: Strangely in some versions its simply called literal: mysql-python.sourceforge.net/MySQLdb-1.2.2/public/…
Nah, that is an internal function for MySQLdb. You are supposed to use the syntax you used in the answer. It's in the source Luke! ` def _get_string_literal(): def string_literal(obj, dummy=None): return db.string_literal(obj) return string_literal def _get_unicode_literal(): def unicode_literal(u, dummy=None): return db.literal(u.encode(unicode_literal.charset)) return unicode_literal `
1

Another answer that I don't like particularly, but will work for your apparent use-case:

providers = tuple[str(item) for item in providers] # ('provider1', 'provider2')
# rest of stuff...

SQL = 'SELECT * FROM table WHERE provider IN {}'.format(repr(providers))
cursor.execute(SQL)

2 Comments

Could also write '...{!r}.format(providers) I guess - just depends on taste
This option is not good because if there is only 1i item in the list there will be a comma at the end which is invalid syntax
0

You should probably do the string replacement before passing it to the cursor object to execute:

sql = "SELECT * FROM table WHERE provider IN (%s)" % \
        (','.join(str(x) for x in providers))
cursor.execute(sql)

4 Comments

This may be problematic because the strings are not quoted. Maybe replace str with repr ?
could also add quotes to the join eg. %('"'+'","'.join(str(x) for x in providers)+'"') - that could be seen as a bit hack though I suppose
But I think its required for this solution.
FWIW, this method is susceptible to SQL injection attacks. The accepted answer (by jdi) is safer.
0

So, you have string input for ID's required:

some_vals = '1 3 5 76 5 4 2 5 7 8'.split() # convert to suitable type if required
SomeModel.objects.filter(provider__in=some_vals)

3 Comments

Yea I asked about this in the main comments. OP said there is a specific reason they are going for a raw query
@jdi Interesting - if the object is inside the ORM, the SQL query can be retrieved from this answer. Otherwise, I can only think they're querying something outside the ORM.
I am totally with you on that, for sure. We can only assume the OP has needs that the ORM simply can't meet.
-1
"SELECT * FROM table WHERE provider IN ({0},{1},{2})".format(*args) #where args is list or tuple of arguments.

1 Comment

This is a little bit limited since it assumes the length of the list, right?
-2

try this.... should work.

SQL = "SELECT * FROM table WHERE provider IN %s"%(providers)
exec 'cursor.execute("%s")'%(SQL)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.