实体的事件以下列方式存储:
class Event(db.Model):
__tablename__ = 'event'
id = db.Column(db.Integer, primary_key=True)
entity_id = db.Column(db.Integer, db.ForeignKey('entity.id'))
mode = db.Column(db.Integer)
timestamp = db.Column(db.DateTime, default=datetime.datetime.utcnow)
duration = db.Column(db.Integer, nullable=False, default=0, server_default=db.text('0'))
记录在事件开始时添加,当然没有持续时间。如果存在不同模式的先前事件,则需要设置持续时间。
一个mode
None
的示例,最终结果断言:
assert [(x.timestamp, x.duration, x.mode) for x in events] == [
(datetime.datetime(2018, 2, 22, 10, 23, 45), 120L, 0L),
(datetime.datetime(2018, 2, 22, 10, 25, 45), 172800L, 1L),
(datetime.datetime(2018, 2, 23, 10, 25, 45), 0L, None),
(datetime.datetime(2018, 2, 24, 10, 25, 45), 0L, 2L)
]
持续时间用于所涉及的统计报告,因此最好将它们存储起来。事件在创建后是只读的(除了可能稍后设置持续时间),事件表是一种日志。另一个假设是,小持续时间并不重要,因此如果发生一些短事件突发,它不会伤害接近零的持续时间,但是,长事件必须有持续时间(除非事件尚未结束)。当然,这仍然是有效的并发问题。
(可能)用持续时间更新旧事件的最佳方法是什么?
我想到的一些选项是:
__init__
调用类方法,该方法查询同一实体的上一个事件,如果找到则设置其持续时间答案 0 :(得分:1)
这不是最好的方式,也不是非常聪明,但它允许计算持续时间而不实现它们。我以为我会把它作为一种好奇心来提供。
使用window functions - 来自MySQL 8 - 可以计算给定窗口中相邻行之间的差异:
SELECT
id,
entity_id,
mode,
"timestamp",
CASE
WHEN NOT same_mode THEN
COALESCE(
TIMESTAMPDIFF(SECOND,
"timestamp",
LEAD("timestamp") OVER w),
0)
ELSE 0
END AS duration
FROM (
SELECT
*,
mode IS NULL OR
LAG(mode) OVER v IS NOT NULL AND
mode = LAG(mode) OVER v AS same_mode
FROM event
WINDOW v AS (PARTITION BY entity_id ORDER BY "timestamp")) e
WINDOW w AS (PARTITION BY entity_id, same_mode
ORDER BY "timestamp")
ORDER BY "timestamp", ISNULL(mode), mode;
SQLAlchemy中的相同:
In [3]: lagged_mode = db.func.lag(Event.mode).\
...: over(partition_by=Event.entity_id,
...: order_by=Event.timestamp)
In [4]: subquery = db.session.query(
...: Event,
...: ((Event.mode == None) |
...: (lagged_mode != None) &
...: (Event.mode == lagged_mode)).label('same_mode')).\
...: subquery()
In [5]: event_alias = db.aliased(Event, subquery)
In [6]: led_timestamp = db.func.lead(event_alias.timestamp).\
...: over(partition_by=(event_alias.entity_id,
...: subquery.c.same_mode),
...: order_by=event_alias.timestamp)
In [7]: query = db.session.query(
...: event_alias,
...: db.case(
...: [(~subquery.c.same_mode,
...: db.func.coalesce(
...: db.func.timestampdiff(db.text('second'),
...: event_alias.timestamp,
...: led_timestamp),
...: 0))],
...: else_=0).label('duration')).\
...: order_by(event_alias.timestamp,
...: db.func.isnull(event_alias.mode),
...: event_alias.mode)
...:
使用最新版本的SQLAlchemy,可以将duration
映射为query-time SQL expression,然后由上述查询提供。