Sqlalchemy 按列表中的字段过滤但保持原始顺序?
Sqlalchemy filter by field in list but keep original order?
我有这样的鞋款:
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()
你说 "original order" 是什么意思?数据库没有 "original order" 这样的东西。如果您需要一些订单,您必须添加类似:
.order_by(Shoe.id.desc())
如果您不指定顺序,您仍然有可能从数据库中获取有序数据。但是在这种情况下,数据库只是使用不需要任何不必要的数据操作的顺序。它只是看起来像一个有序的数据,但它不是。
如果您有一个合理的小型 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 的元素,第一个方法将引发异常。
我在使用 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()
干杯
我过去解决这个问题的一种方法是使用 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()
我有这样的鞋款:
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()
你说 "original order" 是什么意思?数据库没有 "original order" 这样的东西。如果您需要一些订单,您必须添加类似:
.order_by(Shoe.id.desc())
如果您不指定顺序,您仍然有可能从数据库中获取有序数据。但是在这种情况下,数据库只是使用不需要任何不必要的数据操作的顺序。它只是看起来像一个有序的数据,但它不是。
如果您有一个合理的小型 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 的元素,第一个方法将引发异常。
我在使用 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()
干杯
我过去解决这个问题的一种方法是使用 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()