1. 程式人生 > >MySQL中資料結果集分頁功能的實現方法;資料庫查詢返回特定結果即分頁查詢

MySQL中資料結果集分頁功能的實現方法;資料庫查詢返回特定結果即分頁查詢

因為欣賞所以轉載 原文地址  http://blog.csdn.net/andkylee/article/details/5637638  http://www.2cto.com/database/201306/218771.html

目前B/S架構的軟體大行其道。通過瀏覽器如何向終端使用者只展示指定頁面內容而不是全部的?畢竟返回全部結果集,時間和空間開銷都很大。

如此情況下,對SQL返回結果集進行分頁是個很不錯的解決方案。

本文主要介紹MySQL資料庫中的分頁實現方式。我以一個數據庫管理員的角度進行說明,至於具體的實現還需要開發人員詳細編寫過程。

總的說來,在MySQL中實現分頁功能是很簡單的事情。MySQL資料庫的開發人員替廣大的使用者著想,提供了一個關鍵字limit來實現限定指定範圍的功能。

下面簡單介紹分頁的思路。

在MySQL5.0.15-nt-max下進行測試

1.新建一張臨時表(MyISAM或者InnoDB型別的都可以),本測試表沒有使用自增列。

  1. CREATE TABLE `andkylee` (  
  2.   `id` int(11) NOT NULL,  
  3.   `name` varchar(30) default NULL,  
  4.   `sex` char(1) NOT NULL,  
  5.   `age` smallint(6) default NULL,  
  6.   `note` varchar(100) default NULL  
  7. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;  

2.插入測試資料。 在這裡尤其介紹一種插入大量測試資料的方法。

先插入2條資料,

  1. insert into andkylee  
  2. values(1,'hanyuliu','F',25,'aaaaaaaaaa'),(2,'andkylee','F',33,'bbbbbbbbbb');  

批量插入資料

在沒有自增列的情況下,用下面的語句來實現插入“重複”資料。

insert into andkylee
select id+(select max(id) from andkylee),name,sex,age,note from andkylee;

本語句能夠複製表已有的所有記錄,並且能夠實現“主鍵列”自增。

可能有點缺陷的是:需要再select列表中逐個寫出各個列名。但是,好像含有自增列的表為了實現插入重複資料,也必須把非自增列都寫出來吧。 如果我說的不對,請告知。

迴圈插入重複資料,使得表資料行數以指數方式增長。 再把已有的資料插入到表內,行數變為以前的2倍。

insert into andkylee
select id+(select max(id) from andkylee),name,sex,age,note from andkylee;
mysql> select count(*) from andkylee;
+----------+
| count(*) |
+----------+
|        4 |
+----------+
1 row in set (0.05 sec)

重複上面的語句,執行10次之後,表內的資料行數為:2048

測試資料情況為:

  1. mysql> select *from andkylee;  
  2. +------+----------+-----+------+------------+  
  3. | id   | name     | sex | age  | note       |  
  4. +------+----------+-----+------+------------+  
  5. |    1 | hanyuliu | F   |   25 | aaaaaaaaaa |  
  6. |    2 | andkylee | F   |   33 | bbbbbbbbbb |  
  7. |    3 | hanyuliu | F   |   25 | aaaaaaaaaa |  
  8. |    4 | andkylee | F   |   33 | bbbbbbbbbb |  
  9. .....  
  10. | 2043 | hanyuliu | F   |   25 | aaaaaaaaaa |  
  11. | 2044 | andkylee | F   |   33 | bbbbbbbbbb |  
  12. | 2045 | hanyuliu | F   |   25 | aaaaaaaaaa |  
  13. | 2046 | andkylee | F   |   33 | bbbbbbbbbb |  
  14. | 2047 | hanyuliu | F   |   25 | aaaaaaaaaa |  
  15. | 2048 | andkylee | F   |   33 | bbbbbbbbbb |  
  16. +------+----------+-----+------+------------+  
  17. 2048 rows inset (0.08 sec)  

3.選擇第100至300行資料

這裡不得不介紹limit這個關鍵字了。

limit關鍵字的使用方式為:

                    (SQL statement)  limit row_position,row_count

其中:(SQL statement) 為完整的SQL語句,就是普通的sql語句,以前的語句該怎麼寫的還是怎麼寫。

limit 是個關鍵字,在MySQL中關鍵字是不區分大小寫的,Limit,limit,LIMIT對於資料庫引擎來說都是一樣的。

後面是兩個引數,row_position表示起始行,更確切的說應該是偏移行,自然row_position是從0開始的。也就是說第一行的偏移行數為0.

要是想返回從第二行開始的記錄,就將row_position指定為1.

第二個引數:row_count表示返回的行數。 這個很簡單,不贅述。

因此,要返回第100至300行資料,需要指定偏移行號為99,並且行數為201.

語句為:

  1. select *from andkylee LimiT 99,201;  

返回結果為:

  1. mysql> select *from andkylee LimiT 99,201;  
  2. +-----+----------+-----+------+------------+  
  3. | id  | name     | sex | age  | note       |  
  4. +-----+----------+-----+------+------------+  
  5. | 100 | andkylee | F   |   33 | bbbbbbbbbb |  
  6. | 101 | hanyuliu | F   |   25 | aaaaaaaaaa |  
  7. | 102 | andkylee | F   |   33 | bbbbbbbbbb |  
  8. | 103 | hanyuliu | F   |   25 | aaaaaaaaaa |  

再展示一個帶where條件的語句的limit使用情況。

返回name為'andkylee'的所有記錄的第100至300行資料。

  1. select *from andkylee where name='andkylee' LimiT 99,201;  

結果為:

  1. mysql> select *from andkylee where name='andkylee' LimiT 99,201;  
  2. +-----+----------+-----+------+------------+  
  3. | id  | name     | sex | age  | note       |  
  4. +-----+----------+-----+------+------------+  
  5. | 200 | andkylee | F   |   33 | bbbbbbbbbb |  
  6. | 202 | andkylee | F   |   33 | bbbbbbbbbb |  
  7. | 204 | andkylee | F   |   33 | bbbbbbbbbb |  
  8. | 206 | andkylee | F   |   33 | bbbbbbbbbb |  
  9. | 208 | andkylee | F   |   33 | bbbbbbbbbb |  
  10. | 210 | andkylee | F   |   33 | bbbbbbbbbb |  
  11. | 212 | andkylee | F   |   33 | bbbbbbbbbb |  
  12. | 214 | andkylee | F   |   33 | bbbbbbbbbb |  
  13. | 216 | andkylee | F   |   33 | bbbbbbbbbb |  
  14. | (0.09 sec)  
  15. mysql> 
 

4. 功能完善。

通過第3步驟中的兩個結果集進行對比,我們可以發現,MySQL中的limit關鍵字類似於一個偽列的功能。比Oracle中的rownum要強大,在oracle中要通過一次rownum再加一次虛擬列才能實現類似MySQL中的limit功能。因此limit功能存在優勢的地方在於:不產生虛擬列。也就是說你不需要花費額外的功夫來處理列名列表了。最簡單的就是直接用星號*,開發人員在編寫存錯過程的時候比較省事了。

最後,個人感覺:通過比較mssqlserver,oracle,sybase中的分頁是實現方式,我感覺MySQL的limit是最簡單最方便的。

---------------------------------------------------------------------------------------------------------------------------------

1 幾種不同資料庫的不同的分頁寫法:

mysql   1 a) 查詢前n條記錄 2 select * from table_name limit 0,n 3 b) 查詢第n條到第m條 4 select * from table_name limit n,m b oracle    1 a)查詢前n條記錄 2 select * from table_name where rownum 3 b)查詢第m條到第n條記錄: 4 select * from (select a.*,a.rownum rn from table_name where rownum<n) where rn>m c sqlserver 1 a)查詢前n條記錄: 2 select top n * from table_name; 3 b)查詢第n條到第m條記錄: 4 select top n * from (select top m * from table_name order by column_name) a order by column_name desc 2 oracle rownum的用法 對於rownum來說它是oracle系統順序分配為從查詢返回的行的編號,返回的第一行分配的是1,第二行是2,依此類推,這個偽欄位可以用於限制查詢返回的總行數,而且rownum不能以任何表的名稱作為字首。   (1) rownum 對於等於某值的查詢條件 如果希望找到學生表中第一條學生的資訊,可以使用rownum=1作為條件。但是想找到學生表中第二條學生的資訊,使用rownum=2結果查不到資料。因為rownum都是從1開始,但是1以上的自然數在rownum做等於判斷是時認為都是false條件,所以無法查到rownum = n(n>1的自然數)。 SQL> select rownum,id,name from student where rownum=1;(可以用在限制返回記錄條數的地方,保證不出錯,如:隱式遊標)   (2)rownum對於大於某值的查詢條件    如果想找到從第二行記錄以後的記錄,當使用rownum>2是查不出記錄的,原因是由於rownum是一個總是從1開始的偽列,Oracle 認為rownum> n(n>1的自然數)這種條件依舊不成立,所以查不到記錄。       那如何才能找到第二行以後的記錄呀。可以使用以下的子查詢方法來解決。注意子查詢中的rownum必須要有別名,否則還是不會查出記錄來,這是因為rownum不是某個表的列,如果不起別名的話,無法知道rownum是子查詢的列還是主查詢的列。 SQL>select * from(select rownum no ,id,name from student) where no>2;   (3)rownum對於小於某值的查詢條件     如果想找到第三條記錄以前的記錄,當使用rownum<3是能得到兩條記錄的。顯然rownum對於rownum<n((n>1的自然數)的條件認為是成立的,所以可以找到記錄。 SQL> select rownum,id,name from student where rownum <3;       綜上幾種情況,可能有時候需要查詢rownum在某區間的資料,那怎麼辦呀從上可以看出rownum對小於某值的查詢條件是人為true的,rownum對於大於某值的查詢條件直接認為是false的,但是可以間接的讓它轉為認為是true的。那就必須使用子查詢。例如要查詢rownum在第二行到第三行之間的資料,包括第二行和第三行資料,那麼我們只能寫以下語句,先讓它返回小於等於三的記錄行,然後在主查詢中判斷新的rownum的別名列大於等於二的記錄行。但是這樣的操作會在大資料集中影響速度。 SQL> select * from (select rownum no,id,name from student where rownum<=3 ) where no >=2;   (4)rownum和排序     Oracle中的rownum的是在取資料的時候產生的序號,所以想對指定排序的資料去指定的rowmun行資料就必須注意了。 SQL> select rownum ,id,name from student order by name;   可以看出,rownum並不是按照name列來生成的序號。系統是按照記錄插入時的順序給記錄排的號,rowid也是順序分配的。為了解決這個問題,必須使用子查詢 SQL> select rownum ,id,name from (select * from student order by name);   3 mysql中的limit用法 具體的語法為:     1 SELECT * FROM table  LIMIT [offset,] rows | rows OFFSET offset     LIMIT 子句可以被用於強制 SELECT 語句返回指定的記錄數。LIMIT 接受一個或兩個數字引數。引數必須是一個整數常量。如果給定兩個引數,第一個引數指定第一個返回記錄行的偏移量,第二個引數指定返回記錄行的最大數目。初始記錄行的偏移量是 0(而不是 1): 為了與 PostgreSQL 相容,MySQL 也支援句法: LIMIT # OFFSET #。    1 mysql> SELECT * FROM table LIMIT 5,10;  // 檢索記錄行 6-15 2   3 //為了檢索從某一個偏移量到記錄集的結束所有的記錄行,可以指定第二個引數為 -1: 4 mysql> SELECT * FROM table LIMIT 95,-1; // 檢索記錄行 96-last. 5   6 //如果只給定一個引數,它表示返回最大的記錄行數目: 7 mysql> SELECT * FROM table LIMIT 5;     //檢索前 5 個記錄行 8   9 //換句話說,LIMIT n 等價於 LIMIT 0,n。   4 mysql的高效分頁寫法 1 Select a.* from ( 2   select id from table b force index(ind_group_type_time) 3   where b.id=1111 order by b.update_time desc limit  xx, xx 4 ) b, table a where a.id=b.id; MySQL的limit工作原理就是先讀取n條記錄,然後拋棄前n條,讀m條想要的,所以n越大,效能會越差。    優化前SQL: SELECT * FROM member ORDER BY last_active LIMIT 50,5    優化後SQL: SELECT * FROM member INNER JOIN (SELECT member_id FROM member ORDER BY last_active LIMIT 50, 5) USING (member_id)    分別在於,優化前的SQL需要更多I/O浪費,因為先讀索引,再讀資料,然後拋棄無需的行。而優化後的SQL(子查詢那條)只讀索引(Cover index)就可以了,然後通過member_id讀取需要的列。   5 分頁寫法的頁數計算 總頁數=(總記錄數-1)/每頁顯示的記錄數 +1   總結一下:資料庫中mysql和oracle的分頁寫法都不一致,各個資料庫有各自的特點。另外要注意下相關sql的效能優化,特別是針對大資料的翻頁查詢。