我发现SQLAlchemy翻译了
db.query(...).filter(A.id.in_(ids))
到
SELECT ...
FROM a
WHERE a.id != a.id
如果ids
为空。这导致在a
表上进行顺序扫描,这对性能来说显然是灾难性的。
第一个问题是:为什么?为什么不只是1 = 0
或任何不需要顺序扫描的东西?
第二个,更重要的是:是否有常用的解决方法(if
附近in_
除外)?
我认为in_
无法轻易重新实现以涵盖所有情况而不会导致该问题,但我不能成为第一个面对它的问题,并且可能有一些解决方案涵盖{{1}的简单常见用例}。
每次SQLAlchemy都会发出警告:
in_
答案 0 :(得分:8)
我正在使用:
if len(ids) > 0:
db.query(...).where(A.id.in_(ids))
else:
db.query(...).where(False)
我尝试了.limit(0)
而不是.where(false)
但没有成功。空查询集中存在一些幕后的差异,这些差异打破了管道中的其他内容。这种解决方法虽然速度更快,但至少可以避免你提到的警告。
答案 1 :(得分:7)
要回答OP的问题"为什么",这里是FAQ entry(我总是难以找到):
为什么
.col.in_([])
会产生col != col
?为什么不1=0
?对该问题的一点介绍。 SQL中的
IN
运算符,给出了一个 要与列进行比较的元素列表,通常不会 接受一个空列表,即有效说:column IN (1, 2, 3)
说:
是无效的column IN ()
SQLAlchemy的
Operators.in_()
运算符,当给出一个空列表时,会生成以下表达式:column != column
从版本0.6开始,它也会产生警告声明 将呈现效率较低的比较操作。这个 表达式是唯一一个既是数据库不可知又产生的表达式 正确的结果。
例如,"的简单方法只是评估为假 比较1 = 0或1!= 1",不能正确处理空值。一种表达 像:
NOT column != column
在
column IS NULL
时不会返回一行,而是一个不考虑该列的表达式,例如:NOT 1=0
将返回一行。
如this post所示,您可以使用ANY函数来避免这种情况,因为即使对于空列表它也在语法上有效(但显然不支持SQLite)。它对于大型列表来说可能更快,因为它可以减少字符串重整以构建查询。
in_
运算符的性能问题为recently been fixed,修复程序可能位于SQLAlchemy 1.2.0中。
答案 2 :(得分:3)
请注意您的要求:
A.id
的值可比较时,任何比较才能真正成功。不存在的值与任何内容都不具有可比性,所有比较将导致不存在的值,而后者又被评估为 False 。也就是说,如果A.ID
为NULL
,则A.ID == anything
False ,A.ID != anything
也 False :{{1如果A.ID == A.ID || A.ID != A.ID
为A.ID
,则 False 。NULL
- 子句询问该值是否为空列表的一部分。不存在的值是无列表的一部分,甚至不是空列表。IN
和某些变体的变体。这是必须检查的条件。不存在的价值不是某种东西;只有一些非IS NOT NULL
的值可以不成为空列表的成员... NULL
- 子句。具体示例为sqlfiddle
有关更哲学的方法,请参阅What is the nature of void
答案 3 :(得分:3)
当我遇到这个时,那是因为我在我的一个数据库表列中使用了Enum类型。当我把它改成String时,问题就消失了。这不是一个真正的解决方案,因为我更喜欢Enum,但确实避免了这个问题。
答案 4 :(得分:1)
使用子查询,如果ids
为空,将(从未执行)。
示例:
subquery = db.query(SomeTable.id).filter(...).subquery()
db.query(...).filter(A.id.in_(subquery))
成:
SELECT ...
FROM a
WHERE a.id IN (SELECT ...)