SQLAlchemy和空IN子句

Bar*_*ski 24 python sql sqlalchemy

我发现SQLAlchemy翻译了

db.query(...).filter(A.id.in_(ids))
Run Code Online (Sandbox Code Playgroud)

SELECT ...
FROM a
WHERE a.id != a.id
Run Code Online (Sandbox Code Playgroud)

如果ids是空的 这导致在a桌面上进行顺序扫描,这对性能来说显然是灾难性的.

第一个问题是:为什么?为什么不只是1 = 0或任何不需要顺序扫描?

第二个,更重要的是:是否有一个常用的解决方法(除了if几乎每个in_)?

我想这in_不能轻易地重新实现,以涵盖所有情况而不会导致这个问题,但我不能成为第一个面对它,可能有一些解决方案涵盖简单,常见的用例in_.

编辑

SQLAlchemy每次发生时都会记录一条警告:

"The IN-predicate on 'foo.bar' was invoked with an empty sequence. This results in a contradiction, which nonetheless can be expensive to evaluate. Consider alternative strategies for improved performance."

The*_*ist 8

我正在使用:

if len(ids) > 0:
    db.query(...).where(A.id.in_(ids))
else:
    db.query(...).where(False)
Run Code Online (Sandbox Code Playgroud)

我尝试过.limit(0)而不是.where(false)没有成功.空查询集中存在一些幕后的差异,这些差异打破了管道中的其他内容.这种解决方法虽然可能更快,但至少可以避免您提到的警告.


qri*_*ris 8

要回答OP关于"为什么"的问题,这里是FAQ条目(我总是很难找到):

为什么.col.in_([])生产col != col?为什么不1=0呢?

对该问题的一点介绍.INSQL中的运算符给出了要与列进行比较的元素列表,通常不接受空列表,也就是说有效说:

column IN (1, 2, 3)
Run Code Online (Sandbox Code Playgroud)

这是无效的说:

column IN ()
Run Code Online (Sandbox Code Playgroud)

SQLAlchemy的Operators.in_()运算符在给定空列表时会生成以下表达式:

column != column
Run Code Online (Sandbox Code Playgroud)

从版本0.6开始,它还会产生一个警告,指出将会呈现效率较低的比较操作.此表达式是唯一一个既与数据库无关又能产生正确结果的表达式.

例如,"仅通过比较1 = 0或1!= 1来评估为假"的简单方法不能正确处理空值.一个表达式:

NOT column != column
Run Code Online (Sandbox Code Playgroud)

不会返回一行column IS NULL,而是一个不考虑列的表达式,例如:

NOT 1=0
Run Code Online (Sandbox Code Playgroud)

将返回一行.

正如所示这篇文章,你可以使用任何功能,避免这种情况,因为它是语法上有效的甚至是空列表(但不支持SQLite的明显).对于大型列表来说,它可能更快,因为它可以减少字符串修改以构建查询.

最近修复了in_运算符的性能问题,修复可能在SQLAlchemy 1.2.0中.


use*_*968 3

请注意您的要求:

  • 只有当 的值A.id具有可比性时,任何比较才能真正成功。不存在的值无法与任何东西进行比较,所有比较都会产生一个不存在的值,该值又被评估为False。也就是说, if A.IDis NULLthenA.ID == anythingFalse并且A.ID != anythingis 也是FalseA.ID == A.ID || A.ID != A.IDis False if A.IDis NULL
  • 带有空序列的 -子句IN询问该值是否是空列表的一部分。不存在的值不属于任何列表,甚至不属于空列表。
  • 因此,您所要求的是 的某种变体,IS NOT NULL并且是虚无的一部分。这是必须检查的情况。不存在的价值不是某种东西,而是某种东西。只有某些值不能NULL成为列表的成员...
  • 由于 sqlalchemy 很聪明地意识到这可能不是您想要表达此条件的方式,因此它会发出警告。IN如果序列为空,您可能应该删除- 子句。

对于一个具体的例子这个sqlfiddle

对于更哲学的方法,请参阅空的本质是什么