在SELECT查询之前获取行

时间:2014-04-30 06:44:21

标签: mysql

我有一张名为" mytable"的表格。列是

Time_Stamp (datetime) PK
Time_stamp_ms (int) PK
data1 (int)
data2 (int)
data3 (int)
data4 (int) 
data5 (int)
data6 (int)
cycle (int)
name (varstring)

我想通过Time_Stamp和Time_stamp_ms(我知道如何从另一个问题执行此操作)进行排序,然后每个时间周期达到1,我想从上一行获取Time_Stamp和Time_Stamp_ms。周期是1,2,3,4 ...... n表示它总是递增1。

这张表可能会有数百万行。

也没有PHP。

我的桌子有一个样本:

Time_Stamp              Time_Stamp_ms   d1      d2      d3      d4      d5      d6      cycle   name    

2014-04-24 09:09:37         765         5555    4444    3333    2222    1111    123     1       name
2014-04-24 09:09:37         845         5555    4444    3333    2222    1111    123     2       name
2014-04-24 09:09:37         925         5555    4444    3333    2222    1111    123     3       name
2014-04-24 09:09:38         5           5555    4444    3333    2222    1111    123     4       name
2014-04-24 09:09:38         85          5555    4444    3333    2222    1111    123     5       name
2014-04-24 09:09:38         165         5555    4444    3333    2222    1111    123     6       name
2014-04-24 09:09:38         245         5555    4444    3333    2222    1111    123     7       name
2014-04-24 09:09:38         325         5555    4444    3333    2222    1111    123     8       name
2014-04-24 09:09:38         405         5555    4444    3333    2222    1111    123     9       name
2014-04-24 09:09:38         485         5555    4444    3333    2222    1111    123     10      name
2014-04-24 09:09:38         565         5555    4444    3333    2222    1111    123     11      name
2014-04-24 09:09:38         645         5555    4444    3333    2222    1111    123     12      name
2014-04-24 09:09:38         725         5555    4444    3333    2222    1111    123     13      name
2014-04-24 09:09:38         805         5555    4444    3333    2222    1111    123     1       name
2014-04-24 09:09:38         885         5555    4444    3333    2222    1111    123     2       name
2014-04-24 09:09:38         965         5555    4444    3333    2222    1111    123     3       name
2014-04-24 09:09:39         45          5555    4444    3333    2222    1111    123     4       name
2014-04-24 09:09:39         125         5555    4444    3333    2222    1111    123     5       name
2014-04-24 09:09:39         205         5555    4444    3333    2222    1111    123     6       name
2014-04-24 09:09:39         285         5555    4444    3333    2222    1111    123     1       name
2014-04-24 09:09:39         365         5555    4444    3333    2222    1111    123     2       name
2014-04-24 09:09:39         445         5555    4444    3333    2222    1111    123     3       name
2014-04-24 09:09:39         525         5555    4444    3333    2222    1111    123     4       name
2014-04-24 09:09:39         605         5555    4444    3333    2222    1111    123     5       name
2014-04-24 09:09:39         685         5555    4444    3333    2222    1111    123     6       name
2014-04-24 09:09:39         765         5555    4444    3333    2222    1111    123     1       name
2014-04-24 09:09:39         845         5555    4444    3333    2222    1111    123     2       name
2014-04-24 09:09:39         925         5555    4444    3333    2222    1111    123     3       name

应该回复我:

    Time_Stamp              Time_Stamp_ms   d1      d2      d3      d4      d5      d6      cycle   name    


2014-04-24 09:09:38         725         5555    4444    3333    2222    1111    123     13      name
2014-04-24 09:09:39         205         5555    4444    3333    2222    1111    123     6       name
2014-04-24 09:09:39         685         5555    4444    3333    2222    1111    123     6       name

5 个答案:

答案 0 :(得分:4)

如评论中所述,您确实需要一个指示行顺序的字段。 pkey int primary key auto_increment字段不保证最新的行始终具有最大的id,因此严格来说,100%的时间不起作用。包含精确插入时间的列可以。

假设(我知道错误),您的值字段是可以排序的字段,此查询将获取id = 1之前的每一行。要获得正确的结果,请创建一个有序的字段,并将value替换为两个order by子句中的该字段

更新了查询:http://sqlfiddle.com/#!2/9cf7d1/1/0

SELECT Time_Stamp, Time_stamp_ms, cycle FROM (
    SELECT 
        COALESCE((@preVal=1), 0) AS afterOne, 
        m.*, 
        @preVal:=m.cycle  
    FROM mytable as m, 
    (SELECT @preVal:=NULL) AS d 
    ORDER BY Time_Stamp desc, Time_stamp_ms desc
) t 
WHERE afterOne = 1 
ORDER BY Time_Stamp, Time_stamp_ms;

另外一个注意事项。如果您正在处理大数据集,则可以通过将内部查询插入临时表,索引afterOne,然后选择最终结果来发现性能大幅提升。 MySQL因为subquerys的速度慢而臭名昭着。

PS。嗯,我现在看到我可能选择了不好,之后真的意味着在命令提升之前。哦,无论如何,它的占位符可以命名为任何有意义的东西。

答案 1 :(得分:3)

正如mcalex所说

您没有主键。 2.数据的行顺序应该无关紧要。如果您希望订购这些行,则需要一个字段来帮助完成此操作。那么,你可以在某一行之前要求排

试试这个

SELECT * from
(
  Select @prev As previous,@pid as `Previous id`,@pid := e.id As `id` ,@prev := e.value As current
  From
  (
    Select @prev := null,@pid := 0
  ) As i,tbl As e
) x
Where id=1 And Previous is not null;

<强> Fiddle Demo


<强>输出

+---------------------------------------------------+
|   PREVIOUS |  PREVIOUS_ID |  Current_ID | CURRENT |
+---------------------------------------------------+
|   C        |       3      |        1    |  D      |
|   F        |       3      |        1    |  G      |
|   X        |       4      |        1    |  J      |
+---------------------------------------------------+

答案 2 :(得分:1)

我的第一选择可能是使用上述建议之一生成序列号。然而,有大量记录构建一个类似的序列可能会很慢(特别是如果你忽略了更多的记录)。

然而,另一种选择是进行连接。这很麻烦,因为你有2列来确定哪一个是前一条记录。

没有经过测试但是这样的事情: -

SELECT a.*, b.Time_Stamp, b.Time_stamp_ms
FROM
(
    SELECT a.Time_Stamp, a.Time_stamp_ms, a.cycle, MAX(DATE_ADD(b.Time_Stamp, INTERVAL b.Time_stamp_ms MICROSECONDS)) AS latest_prev_record
    FROM mytable a
    INNER JOIN mytable b
    ON DATE_ADD(a.Time_Stamp, INTERVAL a.Time_stamp_ms MICROSECONDS) > DATE_ADD(b.Time_Stamp, INTERVAL b.Time_stamp_ms MICROSECONDS)
    WHERE a.cycle = 1
    GROUP BY a.Time_Stamp, a.Time_stamp_ms, a.cycle
) Sub1
INNER JOIN mytable a
ON a.Time_Stamp = Sub1.Time_Stamp, 
AND a.Time_stamp_ms = Sub1.Time_stamp_ms, 
AND a.cycle = Sub1.cycle
INNER JOIN mytable b
ON DATE_ADD(b.Time_Stamp, INTERVAL b.Time_stamp_ms MICROSECONDS) = Sub1.latest_prev_record

如果您只想要时间戳而没有其他数据,并且如果您有一个组合的日期/时间/毫秒字段(那么您可以使用子查询),这可以变得非常简单。如果你只是拥有一个连续的id字段的所有记录(即,按顺序排列),那就更容易了。

编辑 - 如果您只希望在第1周期之前返回最后一条记录,则简化: -

SELECT z.*
FROM
(
    SELECT a.Time_Stamp, a.Time_stamp_ms, MAX(DATE_ADD(b.Time_Stamp, INTERVAL b.Time_stamp_ms MICROSECOND)) AS latest_prev_record
    FROM mytable a
    INNER JOIN mytable b
    ON DATE_ADD(a.Time_Stamp, INTERVAL a.Time_stamp_ms MICROSECOND) > DATE_ADD(b.Time_Stamp, INTERVAL b.Time_stamp_ms MICROSECOND)
    WHERE a.cycle = 1
    GROUP BY a.Time_Stamp, a.Time_stamp_ms
) Sub1
INNER JOIN mytable z
ON DATE_ADD(z.Time_Stamp, INTERVAL z.Time_stamp_ms MICROSECOND) = Sub1.latest_prev_record

再次编辑。

您可以为组合时间戳添加一个十进制字段(为其添加索引)并填充: -

update `mytable` set `timestamp_full` =  UNIX_TIMESTAMP(`Time_Stamp`) + (`Time_stamp_ms` / 1000)

然后您可以使用以下SQL来获取所需的记录: -

SELECT z.*
FROM
(
    SELECT a.timestamp_full, MAX(b.timestamp_full) AS latest_prev_record
    FROM mytable a
    INNER JOIN mytable b
    ON a.timestamp_full > b.timestamp_full
    WHERE a.cycle = 1
    GROUP BY a.timestamp_full
) Sub1
INNER JOIN mytable z
ON z.timestamp_full = Sub1.latest_prev_record

答案 3 :(得分:0)

如果它只是一个你正在使用的小表(在10.000行以下),我认为最好的解决方案是获取整个事物并“手动”选择行(=在PHP循环中)。它肯定会比任何基于SQL的解决方案快得多,因为您只需要获取ID和主键来选择结果行。

说到一个严格的基于SQL的解决方案,你需要一个存储过程和一个游标来沿着结果集步进(这允许你退一步) - 但它不是非常有效,因为你需要查询整个表和逐个进行匹配。 基于索引的查询无法执行此操作,因此您获得的任何SQL解决方案都将沿着整个表格进行(执行“完全扫描”),因此不会很快。

AND YES,之前的答案是正确的,因为行的顺序无关紧要。它们有点“随机”,或者至少你应该像对待它们一样看待它们。 (即使您执行ALTER TABLE ... ORDER BY,也无法确定在修改单行的下一个操作之后。)

答案 4 :(得分:0)

作为另一个答案发布,因为它变得越来越复杂,这更像是一个额外选项的讨论。

如果有一个索引列来检查最新记录,或者检查最新的一组循环,那么进行连接会更容易。

如果为循环编号添加一列,则最初可以使用以下内容填充: -

SET @cycle_no = 0;
UPDATE mytable
SET cycle_no=@cycle_no:=@cycle_no + 1
WHERE cycle = 1
ORDER BY time_stamp, time_stamp_ms;

然后

UPDATE mytable a
SET a.cycle_no = (SELECT MAX(b.cycle_no) FROM mytable b WHERE a.time_stamp < b.time_stamp OR (a.time_stamp a.time_stamp < b.time_stamp  b.time_stamp AND a.time_stamp_ms < b.time_stamp_ms ))
WHERE a.cycle != 1

其中第一个填充cycle_no,eacg循环为1,第二个填充所有其他行cycle_no值

您可以使用以下触发器来填充它(可能有更有效的方法)。

CREATE TRIGGER insert_mytable
BEFORE INSERT ON mytable
FOR EACH row
SET NEW.cycle_no = IF(NEW.cycle = 1, (SELECT MAX(cycle_no) + 1 FROM mytable WHERE cycle = 1 ), (SELECT MAX(cycle_no) FROM mytable WHERE cycle = 1 ));

然后你可以得到像这样的最新值(它依赖于cycle_no只递增1): -

SELECT z.*
FROM
(
    SELECT b.cycle_no, MAX(b.cycle)
    FROM mytable a
    INNER JOIN mytable b
    ON b.cycle_no = (a.cycle_no - 1)
    WHERE a.cycle = 1
    GROUP BY b.cycle_no
) Sub1
INNER JOIN mytable z
ON z.cycle_no = Sub1.cycle_no

在测试数据上我敲了一下(大约750万条记录),这需要大约53秒。不确定这是否可以用于你。