MySQL通过与先前组进行比较,根据值的变化选择记录组

时间:2017-11-03 06:05:21

标签: mysql

鉴于该表,我正在尝试选择记录组并对每个组中的最后一列Class求和。分组的规则稍微复杂,行需要相互比较。

|| Seq || Time || Spec || Class  
|| 1   || 8:05 || 0    || 5  
|| 2   || 8:06 || 1    || 5  
|| 3   || 8:07 || 2    ||10  
|| 4   || 8:08 || 4    ||10  
|| 5   || 8:09 || 3    || 5  
|| 6   || 8:10 || 2    || 5  
|| 7   || 8:11 || 6    || 5  
|| 8   || 8:12 || 6    ||15  

我需要根据Spec列中值的变化(增加或减少)对记录进行分组。所需的值更改为2.因此从第1行开始,Spec0。它在第3行之前不会增加至少2。这是一个有效的组,我需要对Class字段求和。预期输出为StartTimeStartSpecEndTimeEndSpecTotalClass

要确定下一组,我需要使用上一组中​​使用的最后一行来衡量值的变化。如您所见,第4行立即增加了2,因此这一行是一个有效的组。

预期产出:

||StartTime || StartSpec || EndTime || EndSpec || TotalClass  
||  8:05    ||    0      ||   8:07  ||    2    ||    20  
||  8:08    ||    4      ||   8:08  ||    4    ||    10  
||  8:09    ||    3      ||   8:10  ||    2    ||    10  
||  8:11    ||    6      ||   8:11  ||    6    ||     5  

1 个答案:

答案 0 :(得分:1)

可以通过使用一些中间变量来检测组中的第一行和最后一行,如下所示。

请注意,如果最后一组尚未关闭,这将“自动关闭”。

另请注意,对于像这样的用例,应用程序级解决方案 可能是一个更优雅的选择(如评论中所述)。

另一种选择是在数据插入时计算显式组鉴别器(即“gid”),并将其存储在表本身中,这样您就可以以标准方式查询数据,而不依赖于任何变量。

SELECT 
    MAX(startTime) as startTime,
    MAX(startSpec) as startSpec,
    MAX(endTime) as endTime,
    MAX(endSpec) as endSpec,
    SUM(class) as totalClass
FROM (
    SELECT 
        /* Detect first and last rows in a group (when ordered by "seq") */
        @first as isFirst,
        @last:=(ABS(@prev-spec)>1 OR seq=(SELECT MAX(seq) FROM groups)) as isLast,    

        /* If this is a first row, set "startTime" and "startSpec" */
        IF(@first,time,NULL) as startTime,
        IF(@first,spec,NULL) as startSpec,

        /* If this is a last row, set "endTime" and "endSpec" */
        IF(@last,time,NULL) as endTime,
        IF(@last,spec,NULL) as endSpec,

        /* Start the next group */
        IF(@last,@prev:=spec,NULL) as nextPrev,
        IF(@last,(@gid:=@gid+1)-1,@gid) as gid,

        /* Flip "first" */
        @first:=@last as nextIsFirst,

        /* Row "class" */
        class
    FROM 

    /* Declare some variables */
    (SELECT @first:=TRUE,@last:=FALSE,@prev:=0,@gid:=0) init 

    CROSS JOIN Groups ORDER BY seq
) labeled GROUP BY gid;