[MySQL] 分組排序取前N條記錄以及生成自動數字序列,類似group by後 limit

mchdba發表於2014-09-10

前言:

        同事的業務場景是,按照cid、author分組,再按照id倒敘,取出前2條記錄出來。

        oracle裡面可以透過row_number() OVER (PARTITION BY cid,author ORDER BY id DESC) 表示根據cid,author分組,在分組內部根據id排序,而此函式計算的值就表示每組內部排序後的順序編號(組內連續的唯一的),而mysql資料庫就沒有這樣的統計函式,需要自己寫複雜的sql來實現。


1,錄入測試資料

  1. USE csdn;
  2. DROP TABLE IF EXISTS test;
  3. CREATE TABLE test (
  4.   id INT PRIMARY KEY,
  5.   cid INT,
  6.   author VARCHAR(30)
  7. ) ENGINE=INNODB;


  8. INSERT INTO test VALUES
  9. (1,1,\'test1\'),
  10. (2,1,\'test1\'),
  11. (3,1,\'test2\'),
  12. (4,1,\'test2\'),
  13. (5,1,\'test2\'),
  14. (6,1,\'test3\'),
  15. (7,1,\'test3\'),
  16. (8,1,\'test3\'),
  17. (9,1,\'test3\'),
  18. (10,2,\'test11\'),
  19. (11,2,\'test11\'),
  20. (12,2,\'test22\'),
  21. (13,2,\'test22\'),
  22. (14,2,\'test22\'),
  23. (15,2,\'test33\'),
  24. (16,2,\'test33\'),
  25. (17,2,\'test33\'),
  26. (18,2,\'test33\');
  27. INSERT INTO test VALUES (200,200,\'200test_nagios\');

2,原始的效率比較低下的子查詢實現方式
SQL程式碼如下:


  1. SELECT * FROM test a
  2. WHERE
  3. N>(
  4.     SELECT COUNT(*)
  5.     FROM test b
  6.     WHERE a.cid=b.cid AND a.`author`=b.`author` AND a.id<b.id
  7. )ORDER BY cid,author,id DESC;

只要將N換成你要的數字比如2,就表示查詢出每個分組的前2條記錄,如下所示:

  1. mysql> SELECT * FROM test a
  2.     -> WHERE
  3.     -> 2>(
  4.     -> SELECT COUNT(*)
  5.     -> FROM test b
  6.     -> WHERE a.cid=b.cid AND a.`author`=b.`author` AND a.id<b.id
  7.     -> )ORDER BY cid,author,id DESC;
  8. +-----+------+----------------+
  9. | id | cid | author |
  10. +-----+------+----------------+
  11. | 2 | 1 | test1 |
  12. | 1 | 1 | test1 |
  13. | 5 | 1 | test2 |
  14. | 4 | 1 | test2 |
  15. | 9 | 1 | test3 |
  16. | 8 | 1 | test3 |
  17. | 11 | 2 | test11 |
  18. | 10 | 2 | test11 |
  19. | 14 | 2 | test22 |
  20. | 13 | 2 | test22 |
  21. | 18 | 2 | test33 |
  22. | 17 | 2 | test33 |
  23. | 200 | 200 | 200test_nagios |
  24. +-----+------+----------------+
  25. 13 ROWS IN SET (0.00 sec)


  26. mysql>


3,使用動態sql來實現
先構造序列號碼,引入一個@row來做rownumber
SET @row=0;SET @mid='';SELECT cid, author, @row:=@row+1 rownum FROM test ORDER BY  cid, author LIMIT 10;   

序列號碼已經出來了,再加一個@mid來進行分組,重點在於CASE WHEN @mid = author THEN @row:=@row+1 ELSE @row:=1 END rownum,表示分組的時候會自動從1計數指導這個分組資料遍歷結束。
SET @row=0;SET @mid='';SELECT cid, author,CASE WHEN @mid = author THEN @row:=@row+1 ELSE @row:=1 END rownum, @mid:=author FROM test ORDER BY cid,author DESC LIMIT 20;  

好了,再外面加一層inner JOIN 再對 rownumber 做限制 就可以拿到目標資料了。
SET @row=0;
SET @mid='';
SELECT a.*,b.rownum FROM test a 
INNER JOIN (
SELECT cid, author, id, CASE WHEN @mid = author THEN @row:=@row+1 ELSE @row:=1 END rownum, @mid:=author MID 
FROM test 
ORDER BY cid,author,id DESC
) b ON b.author=a.author AND b.cid=a.cid AND b.id=a.id  WHERE b.rownum<3;  


執行結果如下所示:

  1. mysql> SET @row=0;
  2. QUERY OK, 0 ROWS affected (0.00 sec)


  3. mysql> SET @mid=\'\';
  4. QUERY OK, 0 ROWS affected (0.00 sec)


  5. mysql> SELECT a.*,b.rownum FROM test a
  6.     -> INNER JOIN (
  7.     -> SELECT cid, author, id, CASE WHEN @mid = author THEN @row:=@row+1 ELSE @row:=1 END rownum, @mid:=author MID
  8.     -> FROM test
  9.     -> ORDER BY cid,author,id DESC
  10.     -> ) b ON b.author=a.author AND b.cid=a.cid AND b.id=a.id WHERE b.rownum<3;
  11. +-----+------+----------------+--------+
  12. | id | cid | author | rownum |
  13. +-----+------+----------------+--------+
  14. | 2 | 1 | test1 | 1 |
  15. | 1 | 1 | test1 | 2 |
  16. | 5 | 1 | test2 | 1 |
  17. | 4 | 1 | test2 | 2 |
  18. | 9 | 1 | test3 | 1 |
  19. | 8 | 1 | test3 | 2 |
  20. | 11 | 2 | test11 | 1 |
  21. | 10 | 2 | test11 | 2 |
  22. | 14 | 2 | test22 | 1 |
  23. | 13 | 2 | test22 | 2 |
  24. | 18 | 2 | test33 | 1 |
  25. | 17 | 2 | test33 | 2 |
  26. | 200 | 200 | 200test_nagios | 1 |
  27. +-----+------+----------------+--------+
  28. 13 ROWS IN SET (0.01 sec)


  29. mysql>

參考文章地址:
http://blog.csdn.net/mchdba/article/details/22163223
http://blog.csdn.net/ylqmf/article/details/39005949










來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26230597/viewspace-1267325/,如需轉載,請註明出處,否則將追究法律責任。

相關文章