Been trying to wrap my head around how to set Duration
in this table, with a single query, that is the difference between the row's timestamp, and the previous row of the same SkillTargetID
value. I found some similar questions already (this one was particularly helpful), but they all were able to predict how far away the second row is, based on a month value, for example. For each row of my data, its "sister" row could be adjacent to it, or not.
Here is a reduced version of my table for this example:
mysql> select * from testdur order by id;
+----+---------------+--------------+----------+
| id | SkillTargetID | UTC_DateTime | Duration |
+----+---------------+--------------+----------+
| 1 | 5000 | 1323719341 | NULL |
| 2 | 5010 | 1323719341 | NULL |
| 3 | 5000 | 1323719342 | NULL |
| 4 | 5010 | 1323719342 | NULL |
| 5 | 5000 | 1323719343 | NULL |
| 6 | 5055 | 1323719345 | NULL |
| 7 | 5010 | 1323719350 | NULL |
| 8 | 5010 | 1323719441 | NULL |
| 9 | 5010 | 1323719444 | NULL |
| 10 | 5000 | 1323719445 | NULL |
| 11 | 5055 | 1323719445 | NULL |
| 12 | 5060 | 1323719445 | NULL |
| 13 | 5000 | 1323719445 | NULL |
| 14 | 5010 | 1323719445 | NULL |
| 15 | 5060 | 1323719446 | NULL |
| 16 | 5000 | 1323719460 | NULL |
| 17 | 5000 | 1323719460 | NULL |
| 18 | 5060 | 1323719500 | NULL |
+----+---------------+--------------+----------+
The base data in the table adheres to this rule: when ordered by id
, the values of UTC_DateTime
will always be greater than or equal to the previous row, as this example data shows. The order of different SkillTargetID
values with the same UTC_DateTime
is not predictable, and many rows will have the same UTC_DateTime
and SkillTargetID
(such as 16 and 17).
The best attempt I have come up with so far includes a subquery to find the previous associated row, if it exists (I also selected the 2nd UTC_DateTime
so you can see what is being subtracted):
SELECT
t.id,
t.SkillTargetID,
t.UTC_DateTime,
t2.UTC_DateTime AS UTC_DateTime2,
(CASE WHEN t2.UTC_DateTime IS NULL THEN 0 ELSE t.UTC_DateTime - t2.UTC_DateTime END) AS Duration
FROM testdur t LEFT JOIN testdur t2
ON t.SkillTargetID = t2.SkillTargetID
AND t2.id = (
SELECT id FROM testdur
WHERE SkillTargetID = t.SkillTargetID AND id < t.id
ORDER BY id DESC
LIMIT 1 )
ORDER BY t.id;
+----+---------------+--------------+---------------+----------+
| id | SkillTargetID | UTC_DateTime | UTC_DateTime2 | Duration |
+----+---------------+--------------+---------------+----------+
| 1 | 5000 | 1323719341 | NULL | 0 |
| 2 | 5010 | 1323719341 | NULL | 0 |
| 3 | 5000 | 1323719342 | 1323719341 | 1 |
| 4 | 5010 | 1323719342 | 1323719341 | 1 |
| 5 | 5000 | 1323719343 | 1323719342 | 1 |
| 6 | 5055 | 1323719345 | NULL | 0 |
| 7 | 5010 | 1323719350 | 1323719342 | 8 |
| 8 | 5010 | 1323719441 | 1323719350 | 91 |
| 9 | 5010 | 1323719444 | 1323719441 | 3 |
| 10 | 5000 | 1323719445 | 1323719343 | 102 |
| 11 | 5055 | 1323719445 | 1323719345 | 100 |
| 12 | 5060 | 1323719445 | NULL | 0 |
| 13 | 5000 | 1323719445 | 1323719445 | 0 |
| 14 | 5010 | 1323719445 | 1323719444 | 1 |
| 15 | 5060 | 1323719446 | 1323719445 | 1 |
| 16 | 5000 | 1323719460 | 1323719445 | 15 |
| 17 | 5000 | 1323719460 | 1323719460 | 0 |
| 18 | 5060 | 1323719500 | 1323719446 | 54 |
+----+---------------+--------------+---------------+----------+
Obviously an UPDATE like this gets nasty real fast as this table grows. This is all I could come up with before going back around in circles:
UPDATE testdur t SET t.Duration = t.UTC_DateTime - (
SELECT UTC_DateTime FROM testdur
WHERE SkillTargetID = t.SkillTargetID AND id < t.id
ORDER BY id DESC LIMIT 1 );
ERROR 1093 (HY000): You can't specify target table 't' for update in FROM clause
What other options do I have?
Here's the test data I was using:
CREATE TABLE `testdur` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`SkillTargetID` int(10) unsigned NOT NULL,
`UTC_DateTime` int(10) unsigned NOT NULL,
`Duration` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO testdur (SkillTargetID,UTC_DateTime) VALUES (5000,1323719341),(5010,1323719341),(5000,1323719342),(5010,1323719342),(5000,1323719343),(5055,1323719345),(5010,1323719350),(5010,1323719441),(5010,1323719444),(5000,1323719445),(5055,1323719445),(5060,1323719445),(5000,1323719445),(5010,1323719445),(5060,1323719446),(5000,1323719460),(5000,1323719460),(5060,1323719500);
BONUS - Is it possible to do this while inserting new multi-row data if it includes the ordered id
already? Such as during:
INSERT INTO testdur (id,SkillTargetID,UTC_DateTime) VALUES
(19,5010,1323719505),
(20,5055,1323719510);
Thanks for the help ahead of time!