Mysql – Select the last logged change before a specified time from a change list

join;MySQLtimestamp

I want to get the last change for all issues issue, that was made before a specified end time. I have 3 tables.

projects

+---+------------+
|ID |project_key |
+---+------------+
|1  |bug_tracker |
+---+------------+

issues

+---+------------+-------------+
|ID |project_key |issue_key    |
+---+------------+-------------+
|1  |bug_tracker |bug_tracker-1|
+---+------------+-------------+
|2  |bug_tracker |bug_tracker-2|
+---+------------+-------------+
|3  |bug_tracker |bug_tracker-3|
+---+------------+-------------+

issue_changes

+---+--------------------+------------+----------+-------------+----------------+
|ID |change_by           |change_time |issue_key |change_field |change_value    |
+---+--------------------+------------+----------+-------------+----------------+
|1  |johny_vegas         |1346422320  |1         |Status       |Created         |
|2  |edward_scissorhands |1346497920  |1         |Status       |Open            |
|3  |harry_potter        |1346546460  |1         |Status       |Resolved        |
|4  |harry_potter        |1346682780  |1         |Status       |Closed          |
|5  |harry_potter        |1346252021  |2         |Status       |Created         |
|6  |harry_potter        |1346252023  |2         |Status       |Open            |
|7  |cmdr_data           |1346338440  |2         |Status       |Owned           |
|8  |luke_skywalker      |1346404980  |2         |Status       |Resolved        |
|9  |johny_rico          |1346419560  |2         |Status       |Open            |
|10 |johny_rico          |1346419560  |2         |Status       |Waive Requested |
|11 |edward_scissorhands |1346433300  |2         |Status       |Open            |
|12 |edward_scissorhands |1346433300  |2         |Status       |Waive Requested |
|13 |edward_scissorhands |1346433300  |2         |Status       |Waived          |
|14 |roger_rabit         |1346252052  |3         |Status       |Created         |
|15 |roger_rabit         |1346252054  |3         |Status       |Open            |
|16 |roger_rabit         |1346255700  |3         |Status       |Owned           |
|17 |cmdr_data           |1346261220  |3         |Status       |Resolved        |
|18 |luke_skywalker      |1346405040  |3         |Status       |Open            |
|19 |johny_vegas         |1346423460  |3         |Status       |Owned           |
+---+--------------------+------------+----------+-------------+----------------+

Now what I expect to get as the result of a query is:

+---+--------------------+------------+----------+-------------+----------------+
|ID |change_by           |change_time |issue_key |change_field |change_value    |
+---+--------------------+------------+----------+-------------+----------------+
|4  |harry_potter        |1346682780  |1         |Status       |Closed          |
|13 |edward_scissorhands |1346433300  |2         |Status       |Waived          |
|19 |johny_vegas         |1346423460  |3         |Status       |Owned           |
+---+--------------------+------------+----------+-------------+----------------+

What I am able to get back is:

+---+--------------------+------------+----------+-------------+----------------+
|ID |change_by           |change_time |issue_key |change_field |change_value    |
+---+--------------------+------------+----------+-------------+----------------+
|1  |johny_vegas         |1346422320  |1         |Status       |Created         |
|5  |harry_potter        |1346252021  |2         |Status       |Created         |
|14 |roger_rabit         |1346252052  |3         |Status       |Created         |
+---+--------------------+------------+----------+-------------+----------------+

The query that I am using is:

SELECT issue1.`issue_key` , changes1.`change_time` , changes1.`change_value` , changes1.`change_field` 
FROM  `issue_changes` AS changes1
INNER JOIN  `issues` AS issue1 ON issue1.`ID` = changes1.`issue_key` 
WHERE issue1.`project_key` =  "bug_tracker"
AND changes1.`change_field` =  "Status"
AND changes1.`change_time` <  "1400110321"
GROUP BY issue1.`issue_key`
ORDER BY changes1.`issue_key` , changes1.`change_time` DESC 

What am I missing? 🙂

Best Answer

PROPOSED QUERY

SELECT B.* FROM
(SELECT MAX(changes1.ID) ID,changes1.issue_key
FROM  `issue_changes` AS changes1
INNER JOIN  `issues` AS issue1 ON issue1.`ID` = changes1.`issue_key` 
WHERE issue1.`project_key` =  "bug_tracker"
AND changes1.`change_field` =  "Status"
AND changes1.`change_time` <  "1400110321"
GROUP BY changes1.issue_key) A
LEFT JOIN issue_changes B USING (ID);

SAMPLE DATA

drop database if exists jamie;
create database jamie;
use jamie

projects

create table projects
(
    ID int not null auto_increment,
    project_key varchar(32),
    primary key (ID)
);
insert into projects (project_key) values ('bug_tracker');

issues

create table issues
(
    ID int not null auto_increment,
    project_key varchar(32),
    issue_key varchar(32),
    primary key (ID),
    KEY (project_key)
);
insert into issues (project_key,issue_key) values
('bug_tracker','bug_tracker-1'),
('bug_tracker','bug_tracker-2'),
('bug_tracker','bug_tracker-3');

issue_changes

create table issue_changes
(
    ID int not null auto_increment,
    change_by    varchar(32),
    change_time  bigint not null,
    issue_key    int not null,
    change_field varchar(32),
    change_value varchar(32),
    primary key (ID)
);
insert into issue_changes
(change_by,change_time,issue_key,change_field,change_value) values
('johny_vegas'        ,1346422320,1,'Status','Created         '),
('edward_scissorhands',1346497920,1,'Status','Open            '),
('harry_potter'       ,1346546460,1,'Status','Resolved        '),
('harry_potter'       ,1346682780,1,'Status','Closed          '),
('harry_potter'       ,1346252021,2,'Status','Created         '),
('harry_potter'       ,1346252023,2,'Status','Open            '),
('cmdr_data'          ,1346338440,2,'Status','Owned           '),
('luke_skywalker'     ,1346404980,2,'Status','Resolved        '),
('johny_rico'         ,1346419560,2,'Status','Open            '),
('johny_rico'         ,1346419560,2,'Status','Waive Requested '),
('edward_scissorhands',1346433300,2,'Status','Open            '),
('edward_scissorhands',1346433300,2,'Status','Waive Requested '),
('edward_scissorhands',1346433300,2,'Status','Waived          '),
('roger_rabit'        ,1346252052,3,'Status','Created         '),
('roger_rabit'        ,1346252054,3,'Status','Open            '),
('roger_rabit'        ,1346255700,3,'Status','Owned           '),
('cmdr_data'          ,1346261220,3,'Status','Resolved        '),
('luke_skywalker'     ,1346405040,3,'Status','Open            '),
('johny_vegas'        ,1346423460,3,'Status','Owned           ');

PROPOSED QUERY EXECUTED

mysql> SELECT B.* FROM
    -> (SELECT MAX(changes1.ID) ID,changes1.issue_key
    -> FROM  `issue_changes` AS changes1
    -> INNER JOIN  `issues` AS issue1 ON issue1.`ID` = changes1.`issue_key`
    -> WHERE issue1.`project_key` =  "bug_tracker"
    -> AND changes1.`change_field` =  "Status"
    -> AND changes1.`change_time` <  "1400110321"
    -> GROUP BY changes1.issue_key) A
    -> LEFT JOIN issue_changes B USING (ID);
+------+---------------------+-------------+-----------+--------------+------------------+
| ID   | change_by           | change_time | issue_key | change_field | change_value     |
+------+---------------------+-------------+-----------+--------------+------------------+
|    4 | harry_potter        |  1346682780 |         1 | Status       | Closed           |
|   13 | edward_scissorhands |  1346433300 |         2 | Status       | Waived           |
|   19 | johny_vegas         |  1346423460 |         3 | Status       | Owned            |
+------+---------------------+-------------+-----------+--------------+------------------+
3 rows in set (0.00 sec)

mysql>

EPILOGUE

The main part of the query is the subquery because it retrieves the maximum ID for each issue_key

SELECT MAX(changes1.ID) ID,changes1.issue_key
FROM  `issue_changes` AS changes1
INNER JOIN  `issues` AS issue1 ON issue1.`ID` = changes1.`issue_key` 
WHERE issue1.`project_key` =  "bug_tracker"
AND changes1.`change_field` =  "Status"
AND changes1.`change_time` <  "1400110321"
GROUP BY changes1.issue_key

Then, I take the result of the subquery and LEFT JOIN the ID to issue_changes

GIVE IT A TRY !!!