[英]Best way to calculate duration of the event in MySQL / SQLAlchemy?
實體的事件以以下方式存儲:
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__
調用類方法,該方法查詢同一實體的上一個事件,如果找到則設置其持續時間 這不是最好的方法,也不是很聰明,但是它允許計算持續時間而不會實現它們。 我以為是出於好奇。
使用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
映射為查詢時的SQL表達式 ,然后由上述查詢提供。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.