SQLAlchemy和空IN子句

时间:2014-05-07 16:20:44

标签: python sql sqlalchemy

我发现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_

5 个答案:

答案 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.IDNULL,则A.ID == anything False A.ID != anything False :{{1如果A.ID == A.ID || A.ID != A.IDA.ID,则 False
  • 带有空序列的NULL - 子句询问该值是否为空列表的一部分。不存在的值是无列表的一部分,甚至不是空列表。
  • 因此,您所要求的是IN某些变体的变体。这是必须检查的条件。不存在的价值不是某种东西;只有一些非IS NOT NULL的值可以成为空列表的成员...
  • 由于sqlalchemy很聪明地说这可能不是你想要表达这种情况的方式,它会发出警告。如果序列为空,您应该删除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 ...)