在使用诸如Integer之类的基本类型时,我可以毫无问题地进行如下查询:
with connection.cursor() as cursor:
cursor.execute(sql='''SELECT count(*) FROM account
WHERE %(pk)s ISNULL OR id %(pk)s''', params={'pk': 1})
如果id = 1
参数等于pk
,则将返回None
的行,或者返回所有行。
但是,当尝试使用类似的方法传递ID的 list / tuple 时,我总是在传递空/无元组时产生SQL语法错误,例如尝试:
with connection.cursor() as cursor:
cursor.execute(sql='''SELECT count(*) FROM account
WHERE %(ids)s ISNULL OR id IN %(ids)s''', params={'ids': (1,2,3)})
有效,但是传递()
会产生SQL语法错误:
psycopg2.ProgrammingError: syntax error at or near ")"
LINE 1: SELECT count(*) FROM account WHERE () ISNULL OR id IN ()
或者如果我通过None
,我会得到:
django.db.utils.ProgrammingError: syntax error at or near "NULL"
LINE 1: ...LECT count(*) FROM account WHERE NULL ISNULL OR id IN NULL
我尝试将参数放在SQL中的()
-(%(ids)s)
中-但这总是会破坏一个或另一个条件。我还尝试过玩pg_typeof
或强制转换参数,但没有结果。
注意:
答案 0 :(得分:1)
注意您可以使用PostgreSQL ANY运算符将Python列表用作IN运算符的参数。
ids = [10, 20, 30]
cur.execute("SELECT * FROM data WHERE id = ANY(%s);", (ids,))
此外,ANY也可以使用空列表,而IN()是SQL语法错误。
答案 1 :(得分:0)
起初我有一个想法,只使用1个参数,但是用一个伪值[-1]
代替它,然后像使用它一样
cursor.execute(sql='''SELECT ... WHERE -1 = any(%(ids)s) OR id = ANY(%(ids)s)''', params={'ids': ids if ids else [-1]})
但是这样做对非空列表进行了全表扫描,这很不幸,所以不行。
然后我想我可以在python中做一些预处理,然后发送2个参数,而不仅仅是单个列表-实际列表和一个空列表布尔指示符。那是
cursor.execute(sql='''SELECT ... WHERE %(empty_ids)s = TRUE OR id = ANY(%(ids)s)''', params={'empty_ids': not ids, 'ids': ids})
这不是最优雅的解决方案,但是它的性能很好(对非空列表进行索引扫描,对空列表进行全表扫描-但是无论如何都会返回整个表,所以可以)
然后最终,我想出了最简单的解决方案,也很优雅:
cursor.execute(sql='''SELECT ... WHERE '{}' = %(ids)s OR id = ANY(%(ids)s)''', params={'ids': ids})
该程序还对非空列表执行索引扫描,因此速度非常快。