查询MySQL记录中的更改

时间:2014-01-30 11:11:31

标签: mysql sql

我有一张这样的表:

id | status | user_id | created_at
---:--------:---------:--------------------
1  |    0   |     1   | 2014-01-05 07:23:15
2  |    1   |     1   | 2014-01-05 07:23:16
3  |    1   |     1   | 2014-01-05 07:23:17
4  |    0   |     1   | 2014-01-05 07:23:18
5  |    0   |     1   | 2014-01-05 07:23:19
6  |    1   |     1   | 2014-01-05 07:23:20
7  |    0   |     2   | 2014-01-05 07:23:21
8  |    0   |     1   | 2014-01-05 07:23:22
9  |    0   |     2   | 2014-01-05 07:23:23
10 |    1   |     2   | 2014-01-05 07:23:24
11 |    0   |     2   | 2014-01-05 07:23:25
12 |    1   |     2   | 2014-01-05 07:23:26

我想查询status字段的变化,按user_id分组,始终获取最后一个状态(基于created_at)。查询的结果应该是这样的:

id | status | user_id | created_at
---:--------:---------:--------------------
1  |    0   |     1   | 2014-01-05 07:23:15
3  |    1   |     1   | 2014-01-05 07:23:17
5  |    0   |     1   | 2014-01-05 07:23:19
6  |    1   |     1   | 2014-01-05 07:23:20
8  |    0   |     1   | 2014-01-05 07:23:22
9  |    0   |     2   | 2014-01-05 07:23:23
10 |    1   |     2   | 2014-01-05 07:23:24
11 |    0   |     2   | 2014-01-05 07:23:25
12 |    1   |     2   | 2014-01-05 07:23:26

有没有办法在这种情况下查询SQL中的更改?应该如何编写此查询?

2 个答案:

答案 0 :(得分:2)

考虑以下内容......

DROP TABLE IF EXISTS my_table;

CREATE TABLE my_table
(id INT NOT NULL AUTO_INCREMENT PRIMARY KEY
,status TINYINT NOT NULL DEFAULT 1
,user_id INT NOT NULL
,created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);

INSERT INTO my_table VALUES
(1  ,   0   ,    1   ,'2014-01-05 07:23:15'),
(2  ,   1   ,    1   ,'2014-01-05 07:23:16'),
(3  ,   1   ,    1   ,'2014-01-05 07:23:17'),
(4  ,   0   ,    1   ,'2014-01-05 07:23:18'),
(5  ,   0   ,    1   ,'2014-01-05 07:23:19'),
(6  ,   1   ,    1   ,'2014-01-05 07:23:20'),
(7  ,   0   ,    2   ,'2014-01-05 07:23:21'),
(8  ,   0   ,    1   ,'2014-01-05 07:23:22'),
(9  ,   0   ,    2   ,'2014-01-05 07:23:23'),
(10 ,   1   ,    2   ,'2014-01-05 07:23:24'),
(11 ,   0   ,    2   ,'2014-01-05 07:23:25'),
(12 ,   1   ,    2   ,'2014-01-05 07:23:26');

对于下面提供的解决方案,id实际上并不重要,因为它是连续的。我把解决方案分解成了一些,这样你就可以看到它正在做什么......

第一部分按用户排名结果......

SELECT x.* 
     , COUNT(*) rank
  FROM my_table x
  JOIN my_table y
    ON y.user_id = x.user_id
   AND y.id <= x.id
 GROUP
    BY x.id
 ORDER
    BY x.user_id,rank;

  +----+--------+---------+---------------------+------+
  | id | status | user_id | created_at          | rank |
  +----+--------+---------+---------------------+------+
  |  1 |      0 |       1 | 2014-01-05 07:23:15 |    1 |
  |  2 |      1 |       1 | 2014-01-05 07:23:16 |    2 |
  |  3 |      1 |       1 | 2014-01-05 07:23:17 |    3 |
  |  4 |      0 |       1 | 2014-01-05 07:23:18 |    4 |
  |  5 |      0 |       1 | 2014-01-05 07:23:19 |    5 |
  |  6 |      1 |       1 | 2014-01-05 07:23:20 |    6 |
  |  8 |      0 |       1 | 2014-01-05 07:23:22 |    7 |
  |  7 |      0 |       2 | 2014-01-05 07:23:21 |    1 |
  |  9 |      0 |       2 | 2014-01-05 07:23:23 |    2 |
  | 10 |      1 |       2 | 2014-01-05 07:23:24 |    3 |
  | 11 |      0 |       2 | 2014-01-05 07:23:25 |    4 |
  | 12 |      1 |       2 | 2014-01-05 07:23:26 |    5 |
  +----+--------+---------+---------------------+------+

第二部分将此查询连接到自身,并突出显示异常......

SELECT a.*
     , b.id
  FROM 
     ( SELECT x.* 
            , COUNT(*) rank
         FROM my_table x
         JOIN my_table y
           ON y.user_id = x.user_id
          AND y.id <= x.id
        GROUP
           BY x.id
     ) a
 LEFT
 JOIN
     ( SELECT x.* 
              , COUNT(*) rank
           FROM my_table x
           JOIN my_table y
             ON y.user_id = x.user_id
            AND y.id <= x.id
          GROUP
             BY x.id
     ) b
    ON b.user_id = a.user_id
   AND b.status  = a.status
   AND b.rank    = a.rank + 1;


    +----+--------+---------+---------------------+------+------+
    | id | status | user_id | created_at          | rank | id   |
    +----+--------+---------+---------------------+------+------+
    |  1 |      0 |       1 | 2014-01-05 07:23:15 |    1 | NULL |
    |  2 |      1 |       1 | 2014-01-05 07:23:16 |    2 |    3 |
    |  3 |      1 |       1 | 2014-01-05 07:23:17 |    3 | NULL |
    |  4 |      0 |       1 | 2014-01-05 07:23:18 |    4 |    5 |
    |  5 |      0 |       1 | 2014-01-05 07:23:19 |    5 | NULL |
    |  6 |      1 |       1 | 2014-01-05 07:23:20 |    6 | NULL |
    |  7 |      0 |       2 | 2014-01-05 07:23:21 |    1 |    9 |
    |  8 |      0 |       1 | 2014-01-05 07:23:22 |    7 | NULL |
    |  9 |      0 |       2 | 2014-01-05 07:23:23 |    2 | NULL |
    | 10 |      1 |       2 | 2014-01-05 07:23:24 |    3 | NULL |
    | 11 |      0 |       2 | 2014-01-05 07:23:25 |    4 | NULL |
    | 12 |      1 |       2 | 2014-01-05 07:23:26 |    5 | NULL |
    +----+--------+---------+---------------------+------+------+

第三部也是最后一部分是故意留给读者的练习,但是,这个解决方案的一个缺点是它不能很好地扩展。

答案 1 :(得分:1)

在这种情况下,在MySQL中使用变量可能是个好主意。

这是一个快速尝试,详细阐述了步骤。清洁并调整它以满足要求和性能。

select id, status, user_id, created_at from
 (select id, status, user_id, created_at,
         (case when @user_id != user_id then 'true' else 'false' end) as user_changed,
         (case when @status  != status then 'true' else 'false' end) as status_changed,
         (case when @user_id != user_id then @user_id := user_id end) as new_user_id,
         (case when @status  != status then @status := status end) as new_status
  from (select * from logs order by user_id asc, created_at desc) l
   join (select @user_id := 0) u
   join (select @status := 0) s) q
where user_changed = 'true' or status_changed = 'true'
order by id
;