簡體   English   中英

在MySQL / SQLAlchemy中計算事件持續時間的最佳方法?

[英]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'))

記錄是在事件開始時添加的,沒有持續時間。 如果先前存在不同模式的事件,則需要設置其持續時間。

一種modeNone示例,聲明了最終結果:

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)
]

持續時間用於涉及的統計報告中,因此最好將它們存儲起來。 創建后,事件是只讀的(除非稍后設置持續時間),事件表有點像日志。 另一個假設是,小的持續時間沒什么大不了的,因此,如果發生短時間突發事件,則不會記錄近零的持續時間,但是,長事件必須具有持續時間(除非事件尚未結束)。 當然,這仍然是有效的並發問題。

(可能)隨着時間的推移更新舊事件的最佳方法是什么?

我想到的一些選擇是:

  1. __init__調用類方法,該方法查詢同一實體的上一個事件,如果找到則設置其持續時間
  2. 使用SQLAlchemy的事件在添加新事件時引起動作
  3. 一些巧妙的表格安排,可以以與存儲的持續時間相同的效率水平計算持續時間

這不是最好的方法,也不是很聰明,但是它允許計算持續時間而不會實現它們。 我以為是出於好奇。

使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM