【问题标题】:Collapsing SQL rows into one row based on match of sequential row pattern根据顺序行模式的匹配将 SQL 行折叠成一行
【发布时间】:2020-09-07 15:04:38
【问题描述】:

我正在开展一个项目,该项目涉及将一系列动作组合成序列。具体的编程问题是,我想弄清楚当这些行与某个序列匹配时,如何使用SQL(最好是mySQL)将表中的多行组合起来。需要注意的是,该表按另外两列分组(谁做的以及他们在哪一天做的)。

对于这个例子,我们正在处理人们早上例行公事的行动列表。我们希望将我们的操作列表组合成两个操作序列:唤醒/打盹闹钟和开始日。唤醒/打盹警报序列是(唤醒行)->(打盹警报)行。开始的一天顺序是(起床行)->(吃早餐行)->(刷牙行)。

我们的原始操作表如下所示:

我们想要的输出如下所示:

到目前为止,我已经使用 mySQL 研究了窗口函数和迭代,但是这两个似乎都没有扩展到我有多个序列要匹配的事实。

我觉得这在 SQL 中可能是不可能的,但我想我会在这里发布,以防其他人知道如何解决这个数据处理问题。最终目标是将这些结果存储在一个视图中,以便我们以后查询操作表时,它会查询“已处理”操作(即,将它们分组为序列后的操作)。

【问题讨论】:

  • 您的逻辑似乎需要对操作进行排序。但是,您的数据没有指定排序的列,因此您的问题对这些数据没有意义。
  • 好点——每个动作还有一个自动递增的 id(以提供序列)。立即更改图片以匹配!

标签: mysql sql sequence data-cleaning data-processing


【解决方案1】:

如果我理解正确,您可以使用lead(),然后使用一些过滤逻辑。首先,分配新的动作:

select t.*,
       (case when (actionid, next_actionid) = ('wokeUp', 'snoozedAlarm')
             then 'wokeup and snoozed'
             when (actionid, next_actionid, next2_actionid) = ('wokeUp', 'ateBreakfast', 'brushedTeeth')
             then 'started day'
             else actionid
         end) as action,
from (select t.*,
             lead(actionId, 1) over (partition by person, day order by id) as next_actionid,
             lead(actionId, 2) over (partition by person, day order by id) as next2_actionid
      from t
     ) t;

接下来,使用此信息进行过滤:

with newactions as (
      select t.*,
             (case when (actionid, next_actionid) = ('wokeUp', 'snoozedAlarm')
                   then 'wokeup and snoozed'
                   when (actionid, next_actionid, next2_actionid) = ('wokeUp', 'ateBreakfast', 'brushedTeeth')
                   then 'started day'
                   else actionid
               end) as action,
             (case when (actionid, next_actionid) = ('wokeUp', 'snoozedAlarm')
                   then 2
                   when (actionid, next_actionid, next2_actionid) = ('wokeUp', 'ateBreakfast', 'brushedTeeth')
                   then 1
                   else 0
               end) as duration
      from (select t.*,
                   lead(actionId, 1) over (partition by person, day order by id) as next_actionid,
                   lead(actionId, 2) over (partition by person, day order by id) as next2_actionid
            from t
           ) t
      )
    select na.*
    from (select na.*,
                 lag(duration) over (partition by person order by id) as prev_duration,
                 lag(duration) over (partition by person order by id) as prev2_duration
          from newactions na
         ) na
    where not (prev_duration >= 1 or
               prev2_duration >= 2
              )

【讨论】:

  • 谢谢!这是一个很好的解决方案。我遇到了一些性能问题(在 56k 行上运行大约需要 13 秒),但是在我最终为人和一天添加索引之后,它加速到了 4 秒!
猜你喜欢
  • 1970-01-01
  • 2015-12-23
  • 1970-01-01
  • 2020-03-20
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 2015-12-10
  • 1970-01-01
相关资源
最近更新 更多