在mysql中利用select语句的一个特性就可以很方便地实现查询结果的分页,select语句的语法:
SELECT
[ALL | DISTINCT | DISTINCTROW ]
[HIGH_PRIORITY]
[STRAIGHT_JOIN]
[SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
[SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
select_expr, ...
[FROM table_references
[WHERE where_condition]
[GROUP BY {col_name | expr | position}
[ASC | DESC], ... [WITH ROLLUP]]
[HAVING where_condition]
[ORDER BY {col_name | expr | position}
[ASC | DESC], ...]
[LIMIT {[offset,] row_count | row_count OFFSET offset}]
[PROCEDURE procedure_name(argument_list)]
[INTO OUTFILE 'file_name' export_options
| INTO DUMPFILE 'file_name'
| INTO @var_name [, @var_name]]
[FOR UPDATE | LOCK IN SHARE MODE]]
LIMIT子句可以用来限制由SELECT语句返回过来的数据数量,它有一个或两个参数,如果给出两个参数,
第一个参数指定返回的第一行在所有数据中的位置(类似于数组或集合中元素的索引—index),从0开始
(注意不是1),第二个参数指定最多返回行数。
例如:
select * from table LIMIT 5,10; #返回第6-15行数据
select * from table LIMIT 5; #返回前5行
select * from table LIMIT 0,5; #返回前5行
[b][size=medium]MySQL5 分页查询深入研究[/size][/b]
按照分页状态划分,MySQL5支持动态分页和静态分页。
按照分页对象划分,MySQL5支持SQL分页和存储过程分页。
MySQL5分页的基本原理是依靠limit字句来对查询分页。
MySQL5的limit语句不支持表达式,只支持确定的整数值或者预定义参数。
这是一个大陷阱!也是MySQL最为变态的一个特征了,让我迷茫了很长时间才知道。
不过DB2、Oracle等数据库分页参数都支持表达式。也是本文中的一个焦点问题。
MySQL5的语句的基本格式是在SELECT语句最后添加一个返回记录的限制数,有两个参数(取值范围均大于等于0),分别用来限制返回记录的起起始位置和返回记录的数量,。但是不包含起始位置的记录,例如:SELECT * FROM TAB LIMIT 1,3; 则指挥显示第2、3、4三条记录,第1条记录不会被返回,如果要返回则应该从0开始。
下面从分页对象的角度研究MySQL5的分页实现,分为SQL分页和存储过程分页。其中SQL分页分为两种类型,一种是静态的,一种是动态的。而存储过程下只讨论动态的,静态的没有意义。
[b]环境:[/b]
Windows XP Professional 简体中文版
mysql-5.0.25.-win32
[b]测试脚本:[/b]
--select语句的语法参考:
CREATE [TEMPORARY] TABLE [IF NOT EXISTS] tbl_name
{ LIKE old_tbl_name | (LIKE old_tbl_name) }
create_definition:
column_definition
| [CONSTRAINT [symbol]] PRIMARY KEY [index_type] (index_col_name,...)
| {INDEX|KEY} [index_name] [index_type] (index_col_name,...)
| [CONSTRAINT [symbol]] UNIQUE [INDEX|KEY]
[index_name] [index_type] (index_col_name,...)
| {FULLTEXT|SPATIAL} [INDEX|KEY] [index_name] (index_col_name,...)
| [CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (index_col_name,...) [reference_definition]
| CHECK (expr)
column_definition:
col_name data_type [NOT NULL | NULL] [DEFAULT default_value]
[AUTO_INCREMENT] [UNIQUE [KEY] | [PRIMARY] KEY]
[COMMENT 'string'] [reference_definition]
-- 行业产业代码表(MYSQL脚本)
DROP TABLE IF EXISTS DM_HY_CY;
CREATE TABLE DM_HY_CY(
XH INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT '序号',
MLDM VARCHAR(1) COMMENT '门类代码',
MLMC VARCHAR(120) COMMENT '门类名称',
CYDM VARCHAR(1) COMMENT '产业代码',
CYMC VARCHAR(30) COMMENT '产业名称'
)ENGINE=MYISAM AUTO_INCREMENT=1 DEFAULT CHARSET=GBK COMMENT '行业产业代码表';
-- 初始化数据(DB2和MySQL通用)
INSERT INTO DM_HY_CY(MLDM,MLMC) VALUES
('A','农、林、牧、渔业'),
('B','采矿业'),
('C','制造业'),
('D','电力、燃气及水的生产和供应业'),
('E','建筑业'),
('F','交通运输、仓储和邮政业'),
('G','信息传输、计算机服务和软件业'),
('H','批发和零售业'),
('I','住宿和餐饮业'),
('J','金融业'),
('K','房地产业'),
('L','租赁和商务服务业'),
('M','科学研究、技术服务和地质勘查业'),
('N','水利、环境和公共设施管理业'),
('O','居民服务和其他服务业'),
('P','教育'),
('Q','卫生、社会保障和社会福利业'),
('R','文化、体育和娱乐业'),
('S','公共管理与社会组织'),
('T','国际组织');
COMMIT;
UPDATE DM_HY_CY
SET CYDM='1',CYMC='第一产业'
WHERE MLDM='A';
COMMIT;
UPDATE DM_HY_CY
SET CYDM='2',CYMC='第二产业'
WHERE MLDM IN('B','C','D','E');
COMMIT;
UPDATE DM_HY_CY
SET CYDM='3',CYMC='第三产业'
WHERE CYDM IS NULL;
COMMIT;
创建数据库表完成后结果如下:
[img]http://fantasyyong840205.iteye.com/upload/picture/pic/7650/0719e154-7e34-3647-8d73-b89418c91779.png [/img]
一、SQL分页
1、静态分页
SELECT XH,MLDM,MLMC FROM DM_HY_CY LIMIT 2,3;
执行结果:
+----+------+------------------------------+
| XH | MLDM | MLMC |
+----+------+------------------------------+
| 3 | C | 制造业 |
| 4 | D | 电力、燃气及水的生产和供应业 |
| 5 | E | 建筑业 |
+----+------+------------------------------+
截图如下:
[img]http://fantasyyong840205.iteye.com/upload/picture/pic/7652/f587ea21-b781-3bf9-8579-52fed09851d9.png [/img]
2、动态分页
[quote]Mysql语法参考:
SQL syntax for prepared statements is based on three SQL statements:
PREPARE stmt_name FROM preparable_stmt
The PREPARE statement prepares a statement and assigns it a name, stmt_name, by which to refer to the statement later.
PREPARE声明预制了一个声明并且给它赋予一个名称stmt_name,今后通过这个名称来引用这个声明。
Statement names are not case sensitive. preparable_stmt is either a string literal or a user variable that contains the text of the statement. The text must represent a single SQL statement, not multiple statements. Within the statement, ‘?’ characters can be used as parameter markers to indicate where data values are to be bound to the query later when you execute it. The ‘?’ characters should not be enclosed within quotes, even if you intend to bind them to string values. Parameter markers can be used only where data values should appear, not for SQL keywords, identifiers, and so forth.
声明的名称不区分大小写。preparable_stmt可能是一个字符串字面,也可能是用户变量,这个变量包含了声明的正文。声明正文必须实现一个单独的SQL声明,而不是多个声明。在这个声明中,“?”字符可以被用作参数标识来指出参数的数据值。在今后执行声明的时候,这些参数会绑定给查询语句。
If a prepared statement with the given name already exists, it is deallocated implicitly before the new statement is prepared. This means that if the new statement contains an error and cannot be prepared, an error is returned and no statement with the given name exists.
The scope of a prepared statement is the client session within which it is created. Other clients cannot see it.
EXECUTE stmt_name [USING @var_name [, @var_name] ...]
After preparing a statement, you execute it with an EXECUTE statement that refers to the prepared statement name. If the prepared statement contains any parameter markers, you must supply a USING clause that lists user variables containing the values to be bound to the parameters. Parameter values can be supplied only by user variables, and the USING clause must name exactly as many variables as the number of parameter markers in the statement.
You can execute a given prepared statement multiple times, passing different variables to it or setting the variables to different values before each execution.
{DEALLOCATE | DROP} PREPARE stmt_name
To deallocate a prepared statement, use the DEALLOCATE PREPARE statement. Attempting to execute a prepared statement after deallocating it results in an error.
If you terminate a client session without deallocating a previously prepared statement, the server deallocates it automatically.
The following SQL statements can be used in prepared statements: CREATE TABLE, DELETE, DO, INSERT, REPLACE, SELECT, SET, UPDATE, and most SHOW statements. supported. ANALYZE TABLE, OPTIMIZE TABLE, and REPAIR TABLE are supported as of MySQL 5.0.23. Other statements are not yet supported.
The following examples show two equivalent ways of preparing a statement that computes the hypotenuse of a triangle given the lengths of the two sides.
实例1:
The first example shows how to create a prepared statement by using a string literal to supply the text of the statement:
mysql> PREPARE stmt1 FROM 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
mysql> SET @a = 3;
mysql> SET @b = 4;
mysql> EXECUTE stmt1 USING @a, @b;
[b]执行结果:[/b]
+------------+
| hypotenuse |
+------------+
| 5 |
+------------+
mysql> DEALLOCATE PREPARE stmt1;
实例2:
The second example is similar, but supplies the text of the statement as a user variable:
mysql> SET @s = 'SELECT SQRT(POW(?,2) + POW(?,2)) AS hypotenuse';
mysql> PREPARE stmt2 FROM @s;
mysql> SET @a = 6;
mysql> SET @b = 8;
mysql> EXECUTE stmt2 USING @a, @b;
[b]执行结果:[/b]
+------------+
| hypotenuse |
+------------+
| 10 |
+------------+
mysql> DEALLOCATE PREPARE stmt2;
[/quote]
PREPARE STMT1 FROM 'SELECT XH,MLDM,MLMC FROM DM_HY_CY LIMIT ?,?';
SET @START=2;
SET @SIZE=3;
EXECUTE STMT1 USING @START,@SIZE;
说明:
预制语句的SQL语法基于三个SQL语句:
PREPARE stmt_name FROM preparable_stmt;
stmt_name 是预处理语句的标识,是一个将要执行的SQL语句。里面如果有参数,则用“?”替换,“?”在这里也叫占位符。类似JDBC预处理SQL语句。
EXECUTE stmt_name [USING @var_name [, @var_name] ...];
执行一个预处理语句stmt_name,USING表示使用了动态变量(变量名前有“@”标识符),@var_name [, @var_name] ... 是参数列表,按顺序赋值给预处理SQL中的(占位)参数。
{DEALLOCATE | DROP} PREPARE stmt_name;
删除或者分配存储单元给预处理语句。
说明:在MySQL5中,可以使用动态变量,动态变量的类型是不确定的,可以多次赋不同类型的值,动态变量的类型取决于其值的具体类型。动态变量定义的定义和使用就像随地大小便一样,有需要就当即解决。定义的方式也很简单:SET @var_name=...,在定义的时候就给定了值。
执行结果和1一样,截图如下:
[img]http://fantasyyong840205.iteye.com/upload/picture/pic/7654/017afee4-3763-343c-b2bd-b3836cf4401b.png [/img]
二、存储过程分页
--创建存储过程Mysql语法说明:
CREATE
[DEFINER = { user | CURRENT_USER }]
PROCEDURE sp_name ([proc_parameter[,...]])
[characteristic ...] routine_body
CREATE
[DEFINER = { user | CURRENT_USER }]
FUNCTION sp_name ([func_parameter[,...]])
RETURNS type
[characteristic ...] routine_body
proc_parameter:
[ IN | OUT | INOUT ] param_name type
func_parameter:
param_name type
type:
Any valid MySQL data type
characteristic:
LANGUAGE SQL
| [NOT] DETERMINISTIC
| { CONTAINS SQL | NO SQL | READS SQL DATA | MODIFIES SQL DATA }
| SQL SECURITY { DEFINER | INVOKER }
| COMMENT 'string'
-- 设定语句定界符为 $$
DELIMITER $$
-- 预防性删除存储过程
DROP PROCEDURE IF EXISTS testdb.SP_PAGINATION$$
-- 声明创建存储过程
CREATE PROCEDURE SP_PAGINATION(IN IN_START INTEGER(11), IN IN_SIZE INTEGER(11))
-- ------------------------------------------
-- 说明:MySQL5存储过程分页
-- 语言:MySQL
-- 作者:熔 岩
-- 日期:2007-9-1
-- ------------------------------------------
BEGIN
-- 定义两个动态变量,接收存储过程中的分页参数
SET @START=IN_START;
SET @SIZE=IN_SIZE;
-- 定义预处理SQL查询语句
PREPARE STMT FROM 'SELECT MLDM,MLMC FROM DM_HY_CY LIMIT ?,?';
-- 执行预处理语句,并用动态变量替换参数占位符
EXECUTE STMT USING @START,@SIZE;
END
-- 存储过程声明结束
$$
-- 设定语句定界符为 “;”,以保证后来的以分号结尾的SQL正常执行
DELIMITER ;
存储过程部署截图:
[img]http://fantasyyong840205.iteye.com/upload/picture/pic/7656/1b72b533-9dab-3486-93f8-e9828e0cdda8.png [/img]
然后用命令执行:
CALL SP_PAGINATION(2,3);
返回结果如下:
[img]http://fantasyyong840205.iteye.com/upload/picture/pic/7660/ebfa5c1c-13fb-3580-b70c-0671909abfa6.png [/img]
反面教材:如果不使用预处理SQL,不管你怎么搞,这个动态根据传递参数分页都是无法实现的。
下面我就给出我刚开始用DB2的经验实现MySQL5存储过程分页的例子,结果当然是失败了。现在给出来,大家看看:
DELIMITER $$
DROP PROCEDURE IF EXISTS testdb.SP_PAGINATION$$
CREATE PROCEDURE SP_PAGINATION(IN IN_START INTEGER(11), IN IN_SIZE INTEGER(11))
-- ------------------------------------------
-- 说明:反面教材,切勿模仿!MySQL5存储过程分页
-- 语言:MySQL
-- 作者:熔 岩
-- 日期:2007-9-1
-- ------------------------------------------
BEGIN
-- 直接利用调用参数在LIMIT子句中用
SELECT MLDM,MLMC FROM DM_HY_CY LIMIT IN_START,IN_SIZE;
END
$$
DELIMITER ;
不过这个反面教材在DB2下是完全可行的。
[quote]
[b]总结:MySQL5存储过程分页目前我所知道就这几种,也许MySQL会在将来新版本中支持LIMIT的表达式参数,但眼前的是要解决问题。希望这篇文章能对您MySQL过程分页提供一种解决方案,节省摸索研究的时间。也希望各位博友广开言路,提出更多的见解。[/b]
[/quote]