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型別的都可以),本測試表沒有使用自增列。
- CREATE TABLE `andkylee` (
- `id` int(11) NOT NULL,
- `name` varchar(30) default NULL,
- `sex` char(1) NOT NULL,
- `age` smallint(6) default NULL,
- `note` varchar(100) default NULL
-
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
2.插入測試資料。 在這裡尤其介紹一種插入大量測試資料的方法。
先插入2條資料,
- insert into andkylee
- 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
測試資料情況為:
- mysql> select *from andkylee;
- +------+----------+-----+------+------------+
- | id | name | sex | age | note |
- +------+----------+-----+------+------------+
- | 1 | hanyuliu | F | 25 | aaaaaaaaaa |
- | 2 | andkylee | F | 33 | bbbbbbbbbb |
- | 3 | hanyuliu | F | 25 | aaaaaaaaaa |
- | 4 | andkylee | F | 33 | bbbbbbbbbb |
- .....
- | 2043 | hanyuliu | F | 25 | aaaaaaaaaa |
- | 2044 | andkylee | F | 33 | bbbbbbbbbb |
- | 2045 | hanyuliu | F | 25 | aaaaaaaaaa |
- | 2046 | andkylee | F | 33 | bbbbbbbbbb |
- | 2047 | hanyuliu | F | 25 | aaaaaaaaaa |
- | 2048 | andkylee | F | 33 | bbbbbbbbbb |
- +------+----------+-----+------+------------+
- 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.
語句為:
- select *from andkylee LimiT 99,201;
返回結果為:
- mysql> select *from andkylee LimiT 99,201;
- +-----+----------+-----+------+------------+
- | id | name | sex | age | note |
- +-----+----------+-----+------+------------+
- | 100 | andkylee | F | 33 | bbbbbbbbbb |
- | 101 | hanyuliu | F | 25 | aaaaaaaaaa |
- | 102 | andkylee | F | 33 | bbbbbbbbbb |
- | 103 | hanyuliu | F | 25 | aaaaaaaaaa |
再展示一個帶where條件的語句的limit使用情況。
返回name為'andkylee'的所有記錄的第100至300行資料。
- select *from andkylee where name='andkylee' LimiT 99,201;
結果為:
- mysql> select *from andkylee where name='andkylee' LimiT 99,201;
- +-----+----------+-----+------+------------+
- | id | name | sex | age | note |
- +-----+----------+-----+------+------------+
- | 200 | andkylee | F | 33 | bbbbbbbbbb |
- | 202 | andkylee | F | 33 | bbbbbbbbbb |
- | 204 | andkylee | F | 33 | bbbbbbbbbb |
- | 206 | andkylee | F | 33 | bbbbbbbbbb |
- | 208 | andkylee | F | 33 | bbbbbbbbbb |
- | 210 | andkylee | F | 33 | bbbbbbbbbb |
- | 212 | andkylee | F | 33 | bbbbbbbbbb |
- | 214 | andkylee | F | 33 | bbbbbbbbbb |
- | 216 | andkylee | F | 33 | bbbbbbbbbb |
- | (0.09 sec)
- mysql>
4. 功能完善。
通過第3步驟中的兩個結果集進行對比,我們可以發現,MySQL中的limit關鍵字類似於一個偽列的功能。比Oracle中的rownum要強大,在oracle中要通過一次rownum再加一次虛擬列才能實現類似MySQL中的limit功能。因此limit功能存在優勢的地方在於:不產生虛擬列。也就是說你不需要花費額外的功夫來處理列名列表了。最簡單的就是直接用星號*,開發人員在編寫存錯過程的時候比較省事了。
最後,個人感覺:通過比較mssqlserver,oracle,sybase中的分頁是實現方式,我感覺MySQL的limit是最簡單最方便的。
---------------------------------------------------------------------------------------------------------------------------------
1 幾種不同資料庫的不同的分頁寫法:
a 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的效能優化,特別是針對大資料的翻頁查詢。