1

当使用 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它将返回行或返回所有行。pkNone

但是,当尝试使用类似的方法来传递ID列表/元组时,我总是在传递空/无元组时产生 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或抛出论点,但没有结果。

笔记:

  • 实际的 SQL 要复杂得多,这里的这个是为了说明目的而进行的简化
  • 作为最后的手段 - 我可以根据参数更改 Python 中的 SQL,但我真的想避免这种情况。)
4

2 回答 2

1

来自psycopg2 文档

注意 您可以使用 Python 列表作为使用 PostgreSQL ANY 运算符的 IN 运算符的参数。

ids = [10, 20, 30]
cur.execute("SELECT * FROM data WHERE id = ANY(%s);", (ids,))

此外,ANY 也可以处理空列表,而 IN () 是 SQL 语法错误。

于 2019-06-14T17:28:22.043 回答
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})

这个还对非空列表执行索引扫描,所以它非常快。

于 2019-06-17T21:55:13.137 回答