如何解决加快带有负索引的sqlalchemy orm动态关系切片
我有以下SQLA模型和关系。我每秒记录一次每个通道的测量值,因此数据库中有很多测量值。
class Channel( Model ) :
__tablename__ = 'channel'
id = Column( Integer,primary_key=True )
#! --- Relationships ---
measurements = relationship( 'Measurement',back_populates='channel',lazy='dynamic' )
class Measurement( Model ) :
__tablename__ = 'measurement'
id = Column( Integer,primary_key=True )
timestamp = Column( DateTime,nullable=False )
value = Column( Float,nullable=False )
#! --- Relationships ---
channel = relationship( 'Channel',back_populates='measurements',uselist=False )
如果我想获得最新的测量值,可以通过ORM并使用负索引进行切片。
channel.measurements[-1]
但是,它非常慢!
我可以使用.filter()
和.order_by()
等来进一步过滤关系查询,以获得我想要的东西,但是我喜欢使用ORM(为什么否则呢?)
我注意到,如果使用正索引进行切片,则速度很快(类似于上述的显式SQLA查询)。
channel.measurements[0]
我更改了关系以使measurements
保持相反的顺序,这似乎与使用零索引结合使用。
measurements = relationship( 'Measurement',lazy='dynamic',order_by='Measurement.id.desc()' )
那么,为什么负索引切片这么慢?
这是SQLAlchemy中的错误吗?我本以为执行正确的SQL从数据库中仅获取最新项目足够聪明?
我还需要做其他事情以使测量结果按自然顺序排序并使用负索引切片并获得与其他方法相同的速度吗?
解决方法
您没有进行任何排序,因此必须将所有对象加载到列表中,然后获取最后一个。
如果添加echo=True
参数,则可以看到查询的区别:
对于measurements[0]
,它仅选择与通道匹配的测量值之一(LIMIT 1
:
SELECT measurement.id AS measurement_id,measurement.ts AS measurement_ts,measurement.value AS measurement_value,measurement.channel_id AS measurement_channel_id
FROM measurement
WHERE %(param_1)s = measurement.channel_id
LIMIT %(param_2)s
{'param_1': 6,'param_2': 1}
对于measurements[-1]
,它选择与通道匹配的所有测量值。您尚未排序,因此它必须要求数据库以它决定的顺序返回行(也许是measurement
上的主键,但不能保证):
SELECT measurement.id AS measurement_id,measurement.channel_id AS measurement_channel_id
FROM measurement
WHERE %(param_1)s = measurement.channel_id
{'param_1': 6}
如果只需要最新的测量,请选择它并按时间戳字段排序;您可能希望在channel_id
和timestamp
字段上建立索引:
db.session.query(Measurement)\
.filter(Measurement.channel_id == channel_id)\
.order_by(Measurement.ts.desc())\
.limit(1)\
.first()
,
似乎答案是,SQLA不支持带有负索引的有效切片或房地产集合。实际上,代码中似乎存在一些混乱的尝试,但是由于没有仔细考虑,因此将其从SQLA中删除。
https://github.com/sqlalchemy/sqlalchemy/issues/5605
我已经实现了混合属性,从而向我返回了最新的度量值,而不是直接切片关系集合,从而解决了我的问题。
@hybrid_property
def latest_measurement( self ) -> float :
"""
Hybrid property that returns the latest measurement for the channel.
"""
measurement = self.measurements.order_by( Measurement.id.desc() ).first()
return measurement
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。