I have an implementation for such approach without any selects (but with insert/update). MySQL: 5.7
Here is table structure:
CREATE TABLE UserStatusLog (
`user_id` int(11) NOT NULL,
`status_type` tinyint(8) NOT NULL, // we are logging multiple statuses in one table
`status_value` int(11) NOT NULL, // actual value of the status
`is_last` tinyint(1) DEFAULT '1', // marking flag
`start_ts` timestamp NOT NULL DEFAULT '1970-01-01 00:00:01', // when this status_value became actual
`finish_ts` timestamp NOT NULL DEFAULT '2030-01-01 00:00:00', // end of actuality for this status
`created` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, // just for logging
`updated` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP, // just for logging
PRIMARY KEY (`user_id`,`status_type`,`finish_ts`),
UNIQUE KEY `uniq` (`user_id`,`status_type`,`is_last`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
We are trying to insert new value to log:
INSERT IGNORE INTO UserStatusLog
(user_id, status_type, status_value, is_last, start_ts, finish_ts)
VALUES ($user_id, $type, $value, NULL, '2017-12-08 15:45:34', '2030-01-01 00:00:00')
ON DUPLICATE KEY UPDATE is_last = IF(start_ts < '2017-12-08 15:45:34' && status_value != VALUES(status_value),
2 /* THIS IS A "SPECIAL VALUE" for is_last */
, is_last);
Then, we are comparing result of query via mysql_affected_rows();
- 0 there is a record with start_ts greater than new one, oe
1 this is a first user's record in log, need to perform update, marking is_last for this record:
UPDATE UserStatusLog
SET is_last = 1
WHERE user_id = $user_id AND status_type = $type AND finish_ts = '2030-01-01 00:00:00' LIMIT 1;
2 there was a record, that is elder than new start_ts, and we've updated it with is_last = 2 (SPECIAL VALUE):
UPDATE test.mock_20171208_47d9ac21808ee65d605ca32205888648
SET is_last = NULL, finish_ts = '2017-12-08 15:45:45'
WHERE user_id = $user_id AND status_type = $type AND is_last = 2 LIMIT 1;
// insert new, really last record
INSERT INTO test.mock_20171208_47d9ac21808ee65d605ca32205888648
(user_id, status_type, status_value, is_last, start_ts, finish_ts)
VALUES
($user_id, $type, $value, $is_last = 1, '2017-12-08 15:45:45', '2030-01-01 00:00:00');