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