从给查询结果集增加行号说起


    在Linux下,我们可以用cat -n foo.c输出文件foo.c中的内容,并给每行加上行号。例如:

$ cat -n foo.c  
1  
2 #include <stdio.h>  
3 #include <stdlib.h>  
4  
5 int main(void)  
6 {  
7 printf("Hello, world!\\n");  
8 exit(0);  
9 }  
10

    类似的,我们也可以采用nl -b a foo.c得到同样的结果。例如:

$ nl -b a foo.c  
1  
2 #include <stdio.h>  
3 #include <stdlib.h>  
4  
5 int main(void)  
6 {  
7 printf("Hello, world!\\n");  
8 exit(0);  
9 }  
10

    Shell命令cat和nl的选项以及功能还是有些区别的,具体的差别可以查看man pages。在数据库应用中,有时也需要类似的功能,例如:给结果集中的每条元组依次编号。SQL标准并没有对此做出规定,不同的DBMS产品中实现该功 能的方式也就大相径庭了。

    假设我们所要用的表及其元组为:

CREATE TABLE Student (SNO INT, SName CHAR(8), PRIMARY KEY(SNO));  
INSERT INTO Student VALUES (1, 'Mary');  
INSERT INTO Student VALUES (2, 'Bill');  
INSERT INTO Student VALUES (3, 'Johnson');

    我们所用的DBMS版本为(都是Windows版本):

MySQL 5.0.19  
PostgreSQL 8.1.0  
ORACLE 10g  
MSSQL 2000

MySQL:

mysql> SET @rownum := 0;  
Query OK, 0 rows affected (0.00 sec)
 
mysql> SELECT @rownum := @rownum + 1 AS rownum, SNO, SName FROM Student;  
+--------+-----+---------+  
| rownum | SNO | SName |  
+--------+-----+---------+  
| 1 | 1 | Mary |  
| 2 | 2 | Bill |  
| 3 | 3 | Johnson |  
+--------+-----+---------+  
3 rows in set (0.01 sec)

PostgreSQL:

postgres=# CREATE TEMP SEQUENCE SEQ;  
CREATE SEQUENCE  
postgres=# SELECT NEXTVAL('SEQ'), SNO, SNAME  
postgres-# FROM Student;  
nextval | sno | sname  
---------+-----+----------  
1 | 1 | Mary  
2 | 2 | Bill  
3 | 3 | Johnson  
(3 rows)

ORACLE:

SQL> SELECT ROWNUM, SNO, SNAME  
2 FROM Student;
 
ROWNUM SNO SNAME  
\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-  
1 1 Mary  
2 2 Bill  
3 3 Johnson

MSSQL:

    没有特别简单直观的玩法,MSSQL2000中的各种花样可以参考: http://www.databasejournal.com/features/mssql/article.php/10894_2244821_1。MSSQL2005中的做法可以参考: http://aspnet.4guysfromrolla.com/articles/031506-1.aspx#postadlink

    数据库应用中更多的是需要取出结果集中某个行号范围内的元组,以减少不必要的运行代价。最典型的应该是所谓的TOP N查询,即找出前N个符合要求的元组,而不是返回整个结果集。这些功能显然也可以用ROWNUM来完成。不过DBMS一般还提供了其他的办法。例如:我们 只需要上述结果集中从第二条元组开始的2条元组。

MySQL:

http://dev.mysql.com/doc/refman/5.0/en/select.html

mysql> SELECT SNO, SName  
-\> FROM Student  
-\> LIMIT 2 OFFSET 1;  
+-----+---------+  
| SNO | SName |  
+-----+---------+  
| 2 | Bill |  
| 3 | Johnson |  
+-----+---------+  
2 rows in set (0.00 sec)

PostgreSQL:

postgres=# SELECT SNO, SName  
postgres-# FROM Student  
postgres-# LIMIT 2 OFFSET 1;  
sno | sname  
-----+----------  
2 | Bill  
3 | Johnson  
(2 rows)

ORACLE:

SQL> SELECT * FROM  
2 (SELECT SNO, SName FROM Student ORDER BY SNO)  
3 WHERE ROWNUM < 3;

SNO SNAME  
\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-  
1 Mary  
2 Bill

但是:

SQL> SELECT * FROM  
2 (SELECT SNO, SName FROM Student ORDER BY SNO)  
3 WHERE ROWNUM > 1 AND ROWNUM < 3;

no rows selected

    更多复杂的玩法参见:

http://www.orafaq.com/faqsql.htm
http://www.psoug.org/reference/pseudocols.html
http://www.devx.com/gethelpon/10MinuteSolution/16608

MSSQL:

SELECT TOP 2 SNO, SName  
FROM Student;
 
SNO SName  
\-\-\-\-\-\-\-\-\-\-\- \-\-\-\-\-\-\-\-  
1 Mary  
2 Bill
 
(所影响的行数为 2 行)

    Comparison of different SQL implementations 是一个非常好的参考文献,它提供了不同DBMS之间很多特性的比较。


Copyright 2000-2006,zedware_at_gmail_dot_com
Last modified on Thursday, 2006-05-18

Written on May 18, 2006