I needed to include more than one IN clause with a query along with other named parameters and came up with a helper method that would let me do:
params = {'mn': 5, 'mx': 15}
in_a = sql_in([1,2,3], params, prefix='a')
in_b = sql_in([5,6,7], params, prefix='b')
query = (
"SELECT rowid, name FROM tbl "
"WHERE value BETWEEN :mn AND :mx"
f" AND (alpha IN {in_a} OR beta IN {in_b})"
)
dbcon.execute(query, params)
Which uses this helper method:
def sql_in(values, params, *, prefix='in'):
"""Generate an IN clause for SQLite using named placeholders.
Given values=[1,2,3], will return:
'(:in0,:in1,:in2)'
after doing:
params.update({'in0':1,'in1':2,'in2':3})
If you're using this multiple times with a single dbcon.execute(),
you need to give each one a distinct prefix, e.g.:
params = {}
in_a = sql_in([1,2,3], params, prefix='a')
in_b = sql_in([5,6,7], params, prefix='b')
dbcon.execute(f'SELECT * FROM tbl WHERE a IN {in_a} OR b IN {in_b}', params)
"""
def inner():
yield '('
delim = ':'
for i, val in enumerate(values):
key = f'{prefix}{i}'
assert key not in params
params[key] = val
yield delim
yield key
delim = ',:'
yield ')'
return ''.join(inner())