Python sqlalchemy,将ID列表转换为对象列表
我有要检索的ID序列。很简单:Python sqlalchemy,将ID列表转换为对象列表,python,sqlalchemy,Python,Sqlalchemy,我有要检索的ID序列。很简单: session.query(Record).filter(Record.id.in_(seq)).all() 有更好的方法吗?我建议您看看它生成的SQL。您只需打印str(查询)即可查看它 我不知道用标准SQL实现这一点的理想方法。您的代码非常好 中的就像是一堆X=Y与或连接在一起,在当代数据库中速度非常快 但是,如果ID列表很长,则可以通过传递返回ID列表的子查询来提高查询效率。如果使用复合主键,则可以使用元组,如中所示 from sqlalchemy imp
session.query(Record).filter(Record.id.in_(seq)).all()
有更好的方法吗?我建议您看看它生成的SQL。您只需打印str(查询)即可查看它
我不知道用标准SQL实现这一点的理想方法。您的代码非常好
中的就像是一堆X=Y
与或连接在一起,在当代数据库中速度非常快
但是,如果ID列表很长,则可以通过传递返回ID列表的子查询来提高查询效率。如果使用复合主键,则可以使用元组,如中所示
from sqlalchemy import tuple_
session.query(Record).filter(tuple_(Record.id1, Record.id2).in_(seq)).all()
请注意,这在SQLite上不可用(请参阅)。还有一种方法;如果可以合理预期相关对象已加载到会话中;您以前在同一事务中访问过它们,现在可以执行以下操作:
map(session.query(Record).get, seq)
在这些对象已经存在的情况下,这将快得多,因为不会有任何查询来检索这些对象;另一方面,如果没有加载的对象数量超过一小部分,则加载速度会慢得多,因为这将导致对每个缺少的实例进行查询,而不是对所有对象进行单个查询
当您在执行上述步骤之前执行joinedload()
查询时,这可能非常有用,因此您可以确保这些查询已经加载。一般来说,默认情况下,您应该使用问题中的解决方案,并且只有在您看到一遍又一遍地查询相同的对象时,才能探索此解决方案。原样的代码是完全正确的。然而,有人问我,在对单个ID使用get()的两种方法之间,需要一些对冲系统
如果有人真的试图避免选择,那么最好的方法就是提前在内存中设置所需的对象。例如,您正在处理一个大型元素表。将工作分解为块,例如,按主键或按日期范围排序整个工作集,然后将该块的所有内容本地加载到缓存中:
all_ids = [<huge list of ids>]
all_ids.sort()
while all_ids:
chunk = all_ids[0:1000]
# bonus exercise! Throw each chunk into a multiprocessing.pool()!
all_ids = all_ids[1000:]
my_cache = dict(
Session.query(Record.id, Record).filter(
Record.id.between(chunk[0], chunk[-1]))
)
for id_ in chunk:
my_obj = my_cache[id_]
<work on my_obj>
下面是一个演示:
from sqlalchemy import Column, Integer, create_engine, String
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
import random
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
data = Column(String)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
ids = range(1, 50)
s = Session(e)
s.add_all([A(id=i, data='a%d' % i) for i in ids])
s.commit()
s.close()
already_loaded = s.query(A).filter(A.id.in_(random.sample(ids, 10))).all()
assert len(s.identity_map) == 10
to_load = set(random.sample(ids, 25))
all_ = list(get_all(s, A, to_load))
assert set(x.id for x in all_) == to_load
你不喜欢它的什么地方?它不起作用吗?看起来应该是这样的。它很有效,我只是想知道是否有更好的方法。你说的“更好”是什么意思?您不喜欢这方面的什么?请注意,如果seq
足够长,可能会出现“太多SQL变量”异常,因为IN子句已参数化,并且参数太多。谢谢,这正是我要找的!
from sqlalchemy import Column, Integer, create_engine, String
from sqlalchemy.orm import Session
from sqlalchemy.ext.declarative import declarative_base
import random
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
data = Column(String)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
ids = range(1, 50)
s = Session(e)
s.add_all([A(id=i, data='a%d' % i) for i in ids])
s.commit()
s.close()
already_loaded = s.query(A).filter(A.id.in_(random.sample(ids, 10))).all()
assert len(s.identity_map) == 10
to_load = set(random.sample(ids, 25))
all_ = list(get_all(s, A, to_load))
assert set(x.id for x in all_) == to_load