2012-04-11 67 views
0

一直试图围绕如何在此表中设置Duration包括我的头,与一个查询,这是行的时间戳和前一行的值为SkillTargetID。我发现了一些类似的问题(this one特别有用),但他们都能够预测第二行的距离,例如,基于月份值。对于我的每一行数据,其“姐妹”行可能与它相邻,或者不相邻。设置列之间的差值行之间的行数不确定的行之间的差异

这里是我的表在这个例子中的简化版本:

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 | 
+----+---------------+--------------+----------+ 

该表中的基础数据符合此规则:当id有序的UTC_DateTime值将始终大于或等于到上一行,如这个示例数据所示。具有相同UTC_DateTime的不同SkillTargetID值的顺序不可预测,并且许多行将具有相同的UTC_DateTimeSkillTargetID(如16和17)。

我想出了迄今为止最好的尝试包括子查询找到以前的相关行,如果存在的话(我也选择了第二UTC_DateTime所以你可以看到什么是被减去):

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 | 
+----+---------------+--------------+---------------+----------+ 

很明显,像这样的UPDATE会随着这个表的增长而变得非常快。这是所有我能想出来之前围绕回圈:

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 

我还有什么其他的选择?

下面是我用的测试数据:

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); 

奖金 - 是否有可能做到这一点,同时插入新的多行数据,如果它包括有序id了吗?如期间:

INSERT INTO testdur (id,SkillTargetID,UTC_DateTime) VALUES 
(19,5010,1323719505), 
(20,5055,1323719510); 

感谢您提前帮忙!

回答

1

你可以通过实现@ mysql变量来简化它。预先查询以获得一个ID,以及与它关联的下一个ID,然后对原始表TWICE(不同的别名)进行连接以获得适当的值,然后执行所需的任何计算...因此,“最后一个ID “列是无论@WasLastID值如果它在相同的技能目标ID资格。如果不是,它会被设置回零。在应用TEST之后,@WasLastID获取当前记录的ID作为下一个条目测试的BASIS。

select 
     t.id, 
     t.SkillTargetID, 
     t.UTC_DateTime, 
     t.UTC_DateTime2, 
     if(@WasLastTarget = t.SkillTargetID, @WasLastID, 0) LastID, 
     @WasLastID := t.ID as tmpLastID, 
     @WasLastTarget := t.SkillTargetID as tmpLastTarget 
    from 
     testdur t, 
     (select @WasLastID = 0, 
       @WasLastTarget := 0) sqlvars 
    order by 
     t.skillTargetID, 
     t.id 

通过将应用到结果之前任何@变量设置的顺序处理的,所以上面的查询会在本质上创建以下结果确保所有技能目标应用@最后一个目标时,正确排序比较...

+----+---------------+--------------+--------+------------+---------------+ 
    | id | SkillTargetID | UTC_DateTime | LastID | tmpLastID | tmpLastTarget | 
    +----+---------------+--------------+--------+------------+---------------+ 
    | 1 |   5000 | 1323719341 |  0 |  1  | 5000 
    | 3 |   5000 | 1323719342 |  1 |  3  | 5000 
    | 5 |   5000 | 1323719343 |  3 |  5  | 5000 
    | 10 |   5000 | 1323719445 |  5 |  10  | 5000 
    | 13 |   5000 | 1323719445 | 10 |  13  | 5000 
    | 16 |   5000 | 1323719460 | 13 |  16  | 5000 
    | 17 |   5000 | 1323719460 | 16 |  17  | 5000 
-- BREAK BETWEEN SKILL TARGET... 
    | 2 |   5010 | 1323719341 |  0 |  2  | 5010 
    | 4 |   5010 | 1323719342 |  2 |  4  | 5010 
    | 7 |   5010 | 1323719350 |  4 |  7  | 5010 
    | 8 |   5010 | 1323719441 |  7 |  8  | 5010 
    | 9 |   5010 | 1323719444 |  8 |  9  | 5010 
    | 14 |   5010 | 1323719445 |  9 |  14  | 5010 
-- BREAK BETWEEN SKILL TARGET... 
    | 6 |   5055 | 1323719345 |  0 |  6  | 5055 
    | 11 |   5055 | 1323719445 |  6 |  11  | 5055 
-- BREAK BETWEEN SKILL TARGET... 
    | 12 |   5060 | 1323719445 |  0 |  12  | 5060 
    | 15 |   5060 | 1323719446 | 12 |  15  | 5060 
    | 18 |   5060 | 1323719500 | 15 |  18  | 5060 
    +----+---------------+--------------+--------+------------+---------------+ 

现在,所有的说和样品提供,然后你可以扩大它如...

select 
     PreQuery.*, 
     CASE WHEN t1.UTC_DateTime IS NULL THEN 0 
      ELSE t2.UTC_DateTime - t1.UTC_DateTime END) AS Duration 
    from 
     (above full select query) as PreQuery 
     LEFT JOIN testdur t1 
      on PreQuery.ID = t1.ID 
     LEFT JOIN testdur t2 
      on PreQuery.LastID = t2.ID 
+0

我最后做的大部分的这个代码,而不是SQL,但是哟你的想法确实奏效 - 我测试了它。不过,我可能会将这个想法用于其他方面。感谢您抽出宝贵的时间。 – aparker 2012-04-25 20:18:04