Python SQLAlchemy和空IN子句
我发现炼金术可以翻译成Python SQLAlchemy和空IN子句,python,sql,sqlalchemy,Python,Sql,Sqlalchemy,我发现炼金术可以翻译成 db.query(...).filter(A.id.in_(ids)) 进入 如果ids为空。这会导致对表进行顺序扫描,这显然会对性能造成灾难性影响 第一个问题是:为什么?为什么不只是1=0或任何不需要顺序扫描的东西 第二个更重要的问题是:是否有一种常用的解决方法(除了if之外,在中的每个附近都有) 我想中的不可能很容易地重新实现,以覆盖所有情况而不导致该问题,但我不能第一个面对它,并且可能有一些解决方案覆盖中的的简单、常见用例 编辑 SQLAlchemy每次发生时都会
db.query(...).filter(A.id.in_(ids))
进入
如果ids
为空。这会导致对表进行顺序扫描,这显然会对性能造成灾难性影响
第一个问题是:为什么?为什么不只是1=0
或任何不需要顺序扫描的东西
第二个更重要的问题是:是否有一种常用的解决方法(除了if
之外,在
中的每个附近都有)
我想
中的不可能很容易地重新实现,以覆盖所有情况而不导致该问题,但我不能第一个面对它,并且可能有一些解决方案覆盖
中的的简单、常见用例
编辑
SQLAlchemy每次发生时都会记录一条警告:
“Fo.bar”的谓词是用一个空序列调用的。这导致了一个矛盾,但这可能是昂贵的评估。考虑替代策略来提高性能。 < /P> < P>意识到你在请求什么:
- 只有当
A.id
的值具有可比性时,任何比较才能真正成功。不存在的值与任何值都不可比较,所有比较将导致不存在的值,该值反过来被评估为False。也就是说,如果A.ID
为NULL
,则A.ID==anything
为False且A.ID!=任何东西都是假的:A.ID==A.ID | | A.ID!=如果A.ID
为NULL
,则A.ID
为False
- 带有空序列的
-子句中的,询问该值是否为空列表的一部分。不存在的值是no列表的一部分,甚至不是空列表
因此,您需要的是的一些变体不是空的
和一些不属于任何内容的东西。这是一种必须检查的情况。不存在的价值不是什么东西;只有非NULL
的某些值才能非成为空列表的成员
由于sqlalchemy巧妙地认识到这可能不是您想要表达这种情况的方式,因此它给出了一个警告。如果序列为空,您可能应该删除
-子句中的
举个具体的例子
有关更具哲理的方法,请参见当我遇到此问题时,这是因为我对数据库表的一列使用了枚举类型。当我把它改成字符串时,问题就消失了。这不是一个真正的解决方案,因为我更喜欢枚举,但它确实避免了问题。我使用:
if len(ids) > 0:
db.query(...).where(A.id.in_(ids))
else:
db.query(...).where(False)
我尝试了.limit(0)
而不是.where(false)
,但没有成功。在空空的querysets中有一些幕后的不同之处,这些querysets破坏了管道中的其他东西。此解决方法虽然可以更快,但至少可以避免您提到的警告。如果ids
为空,则使用subquery(永远不会执行)
例如:
subquery = db.query(SomeTable.id).filter(...).subquery()
db.query(...).filter(A.id.in_(subquery))
进入:
为了回答OP关于“为什么”的问题,这里有一个答案(我总是很难找到):
为什么.col.in.([])
产生col!=col
?为什么不1=0
?
关于这个问题的一点介绍。SQL中的运算符,给定
要与列进行比较的元素列表,通常不会
接受一个空列表,也就是说:
column IN (1, 2, 3)
column IN ()
不能说:
column IN (1, 2, 3)
column IN ()
SQLAlchemy的运算符。在
运算符中,当给定空列表时,会生成以下表达式:
column != column
从版本0.6开始,它还会生成一个警告
将呈现效率较低的比较操作。这
表达式是唯一既与数据库无关又产生
正确的结果
例如,幼稚的方法是“通过
比较1=0或1!=1“,无法正确处理空值。表情
比如:
当列为NULL时,将不返回行,而是返回不考虑该列的表达式,例如:
NOT 1=0
将返回一行
如中所示,您可以使用ANY函数来避免这种情况,因为它在语法上即使对于空列表也是有效的(但SQLite显然不支持)。对于大型列表,它可能也更快,因为它在构建查询时不会对字符串造成太多的损坏
在
操作符中,的性能问题已经解决,修复可能会出现在SQLAlchemy 1.2.0中。关于该主题的有趣文章:。对我来说,最简单、最常用的用例解决方案是if语句。您可能正在(或应该)进行某种验证。只需将空列表添加到您的检查中,不要浪费数据库开销。@LukasGraf该链接不再有效,本文现在是新的FAQ链接
NOT 1=0