Search code examples
mysqlgroup-bysubquerygroup-concat

mysql multiple grouping on one table


I have one table job_result

CREATE TABLE job_result  (
  node varchar(20) DEFAULT NULL,
  jobId int(10) DEFAULT NULL,
  subResult int(1) DEFAULT NULL
) 

The table contains many nodes.

Each node has many jobId.

Each jobId has many subResults (usually less than 10).

Some example data provided

insert into job_result values ('A', 14, 0);
insert into job_result values ('A', 15, 0);
insert into job_result values ('A', 16, 1);
insert into job_result values ('A', 17, 0);
insert into job_result values ('A', 18, 1);
insert into job_result values ('A', 19, 1);
insert into job_result values ('A', 20, 0);


insert into job_result values ('B', 1, 0);
insert into job_result values ('B', 2, 0);
insert into job_result values ('B', 3, 1);
insert into job_result values ('B', 4, 1);
insert into job_result values ('B', 5, 1);
insert into job_result values ('B', 6, 1);
insert into job_result values ('B', 7, 1);


insert into job_result values ('C', 10, 0);
insert into job_result values ('C', 11, 0);
insert into job_result values ('C', 12, 0);
insert into job_result values ('C', 13, 0);
insert into job_result values ('C', 14, 0);
insert into job_result values ('C', 15, 0);
insert into job_result values ('C', 16, 0);

I want a query to give me a "health check" on each node to see if a node has had X consecutively failed jobs. A failed job is a jobId that have at least one non-zero result.

SELECT node, jobId, SUM(subResult) res
FROM job_result WHERE node = 'A' 
GROUP BY jobId 
ORDER BY jobId desc 
LIMIT 5

This gives me the last 5 job's result on node A, where 0 indicates a good job.

+------+---------+------+
| node | jobId   | res  |
+------+---------+------+
| A    |   20    |   0  |
| A    |   19    |   1  |
| A    |   18    |   1  |
| A    |   17    |   0  |
| A    |   16    |   1  |

Then I need to sum and group the results into one row per node and I got this far:

SELECT node, SUM(I.res) res
FROM
(SELECT node, sum(subResult) res
FROM job_result WHERE node = 'A' 
GROUP BY jobId 
ORDER BY jobId desc 
LIMIT 5) I

+------+------+
| node | res  |
+------+------+
| A    |  3   |

My question is: How can I expand this query to return one row per node? I have tried for several days with correlated sub queries and joins, but I always fail due to the inner select cannot see which node to select on. And when that is in place, I will work on changing LIMIT 5 to a dynamic value.

Desired result would be like

+------+------+
| node | res  |
+------+------+
| A    |  3   |
| B    |  0   |
| C    |  1   |
| E    |  0   |

One row for each node, with the right column showing number of fails

Please advise!


Solution

  • In MySQL, on a large table, the best approach to getting the last 5 is to use variables to enumerate the results. The rest is just aggregation:

    SELECT node, SUM(subr > 0) as numfails
    FROM (SELECT node, jobid, subr,
                 (@rn := if(@n = node, @rn + 1,
                            if(@n := node, 1, 1)
                           )
                 ) as rn
          FROM (SELECT node, jobid,
                       SUM(subresult) as subr
                FROM job_result jr
                GROUP BY node, jobid
               ) jr CROSS JOIN
               (SELECT @n := '', @rn := 0) init
          ORDER BY node, jobid desc
         ) nr
    WHERE rn <= 5
    GROUP BY node;