【问题标题】:Sqlalchemy filter by field in list but keep original order?Sqlalchemy 按列表中的字段过滤但保持原始顺序?
【发布时间】:2017-08-25 04:55:43
【问题描述】:

我有一个这样的鞋型:

class Shoe(db.Model):
id = db.Column(db.Integer, primary_key = True)
asin = db.Column(db.String(20), index = True)

我有一个像 ids = [2,1,3] 这样的 id 列表,当我查询 Shoe 模型以使结果在“ids”列表中有 id 时,我想要返回: [{id:2, asin:"111"},{id:1, asin:"113"},{id:3, asin:"42"}] 但问题是使用以下查询语句不保留原来的顺序,结果会随机返回。如何保持我过滤的列表的顺序?

不正确的一个:Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).all()

【问题讨论】:

标签: python sqlalchemy flask-sqlalchemy


【解决方案1】:

如果您有一个合理的小 id 列表,您可以单独对每个 id 执行 SQL 查询:

[Shoe.query.filter_by(id=id).one() for id in my_list_of_ids]

对于大量的id,SQL查询会花费很长时间。然后,您最好使用单个查询并在第二步中将值按正确的顺序排列(借用自 how to select an object from a list of objects by its attribute in python):

shoes = Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).all()
[next(s for s in shoes if s.id == id) for id in my_list_of_ids]

这是假设 id 是唯一的(在您的情况下它们应该是唯一的)。如果有多个具有相同 id 的元素,第一种方法将引发异常。

【讨论】:

    【解决方案2】:

    我过去解决此问题的一种方法是使用SQL CASE expression 告诉数据库我希望返回行的顺序。使用您的示例:

    from sqlalchemy.sql.expression import case
    
    ordering = case(
        {id: index for index, id in enumerate(my_list_of_ids)},
        value=Shoe.id
     )
    Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).order_by(ordering).all()
    

    【讨论】:

    • 我发现没有orderingcase 的用户我可以做到这一点。只需在all() 之前加上order_by,并使用我通常使用filter_by(w/e).order_by(arg) 的参数作为参数。例如:filter(w/e).order_by(desc(User.id)).all().
    • 这仅在您想按 id 字段排序时才有效,如果您需要在 my_list_of_ids 中保留预先存在的排序则无效。
    • 这看起来真是完美的答案。
    【解决方案3】:

    我在使用 MySQL 数据库时也遇到了同样的问题。这就是我所做的:

    my_list = [13,14,5,6,7]
    # convert my_list to str
    my_list_str = ','.join(map(str, my_list))
    

    这就是我的查询的样子:

    checkpoints = (
        db_session.query(Checkpoint)
        .filter(Checkpoint.id.in_(my_list))
        .order_by('FIELD(id, ' + my_list_str + ')')
        .all()
    )
    

    FIELD() 是 MySQL 中的原生函数。

    编辑:所以您的查询应该如下所示:

    my_list_of_ids_str = ','.join(map(str, my_list_of_ids)) 
    Shoe.query.filter(Shoe.id.in_(my_list_of_ids)).order_by('FIELD(id, ' + my_list_of_ids_str + ')').all()
    

    干杯

    【讨论】:

      【解决方案4】:

      你说的“原始订单”是什么意思?数据库没有“原始订单”之类的东西。如果你需要一些订单,你必须添加类似的东西:

      .order_by(Shoe.id.desc())
      

      如果不指定顺序,仍然有可能从数据库中获取有序数据。但是在这种情况下,数据库只使用不需要任何不必要的数据操作的顺序。它只是看起来像一个有序的数据,但它不是。

      【讨论】:

        猜你喜欢
        • 2021-06-09
        • 2020-04-18
        • 2016-11-19
        • 1970-01-01
        • 1970-01-01
        • 2019-04-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多