MySQL ORDER BY
通常情况下,SELECT
语句返回的结果集是按照数据库默认的规则排序的。如果我们想按照自定义自定义规则排序结果集,可以使用 ORDER BY
子句。
我们可以通过 ORDER BY
子句指定排序的字段以及升序排序还是降序排序。
MySQL ORDER BY 语法
在 ORDER BY
子句中,我们可以指定一个或多个排序的字段。 ORDER BY
子句的语法如下:
sql
SELECT
column1, column2, ...
FROM
table_name
[WHERE clause]
ORDER BY
column1 [ASC|DESC],
column2 [ASC|DESC],
...;
ORDER BY
子句可以指定一个或多个字段。
[ASC|DESC]
代表排序是升序还是降序,这是可选的。
ASC
代表升序,DESC
代表降序。
未指定 [ASC|DESC]
时,默认值是 ASC
。即,默认是按指定的字段升序排序。
当指定多个列时,首先按照前面的字段排序,其次按照后面的字段排序。
MySQL ORDER BY 排序规则说明
ORDER BY column ASC;
此 ORDER BY
子句对结果集按 column
字段的值升序排序。
ORDER BY column DESC;
此 ORDER BY
子句对结果集按 column
字段的值降序排序。
ORDER BY column;
此 ORDER BY
子句对结果集按 column
字段的值升序排序。这个语句等效于: ORDER BY column ASC;
。
ORDER BY column1, column2;
此 ORDER BY
子句对结果集先按 column1
字段的值升序排序,然后再按 column2
字段的值升序排序。
也就是说主排序按 column1
字段升序排序,在主排序的基础上,对 column1
字段相同的行,再按 column2
字段升序排序。
ORDER BY column1 DESC, column2;
此 ORDER BY
子句对结果集先按 column1
字段的值降序排序,然后再按按 column2
字段的值升序排序。
也就是说主排序按 column1
字段降序排序,在主排序的基础上,对 column1
字段相同的行,再按 column2
字段升序排序。
MySQL ORDER BY 实例
在以下实例中,我们使用 Sakila 示例数据库中的 actor
表进行演示。
vbnet
SELECT actor_id, first_name, last_name FROM actor ORDER BY last_name;
diff
+----------+-------------+--------------+
| actor_id | first_name | last_name |
+----------+-------------+--------------+
| 92 | KIRSTEN | AKROYD |
| 58 | CHRISTIAN | AKROYD |
| 182 | DEBBIE | AKROYD |
| 194 | MERYL | ALLEN |
| 118 | CUBA | ALLEN |
| 145 | KIM | ALLEN |
...
按字段降序排序
以下 SQL 语句使用 ORDER BY
子句按演员姓氏降序进行排序。
sql
SELECT
actor_id, first_name, last_name
FROM
actor
ORDER BY last_name DESC;
diff
+----------+-------------+--------------+
| actor_id | first_name | last_name |
+----------+-------------+--------------+
| 111 | CAMERON | ZELLWEGER |
| 85 | MINNIE | ZELLWEGER |
| 186 | JULIA | ZELLWEGER |
| 63 | CAMERON | WRAY |
| 156 | FAY | WOOD |
| 13 | UMA | WOOD |
| 144 | ANGELA | WITHERSPOON |
| 68 | RIP | WINSLET |
....
按多字段排序
以下 SQL 语句使用 ORDER BY
子句先按演员姓氏升序排序,再按演员名字升序排序。
vbnet
SELECT
actor_id, first_name, last_name
FROM
actor
ORDER BY last_name, first_name;
diff
+----------+-------------+--------------+
| actor_id | first_name | last_name |
+----------+-------------+--------------+
| 58 | CHRISTIAN | AKROYD |
| 182 | DEBBIE | AKROYD |
| 92 | KIRSTEN | AKROYD |
| 118 | CUBA | ALLEN |
| 145 | KIM | ALLEN |
| 194 | MERYL | ALLEN |
....
按自定义顺序排序
有时候单纯的按照字段的值排序并不能满足要求,我们需要按照自定义的顺序的排序。比如,我们需要按照电影分级 'G', 'PG', 'PG-13', 'R', 'NC-17'
的顺序对影片进行排序。
对于这样的需求,它可以理解为按照列表中元素的索引位置进行排序。我们分别使用 CASE
子句或 FIELD()
函数实现它。
在以下实例中,我们使用 Sakila 示例数据库中的 film
表作为演示。
假设您要根据影片的等级按照的 'G', 'PG', 'PG-13', 'R', 'NC-17'
顺序对影片进行排序。
vbnet
SELECT
film_id, title, rating
FROM
film
ORDER BY CASE rating
WHEN 'G' THEN 1
WHEN 'PG' THEN 2
WHEN 'PG-13' THEN 3
WHEN 'R' THEN 4
WHEN 'NC-17' THEN 5
END;
erlang
+---------+-----------------------------+--------+
| film_id | title | rating |
+---------+-----------------------------+--------+
| 2 | ACE GOLDFINGER | G |
| 4 | AFFAIR PREJUDICE | G |
...
| 1 | ACADEMY DINOSAUR | PG |
| 6 | AGENT TRUMAN | PG |
...
| 7 | AIRPLANE SIERRA | PG-13 |
| 9 | ALABAMA DEVIL | PG-13 |
...
| 8 | AIRPORT POLLOCK | R |
| 17 | ALONE TRIP | R |
...
| 3 | ADAPTATION HOLES | NC-17 |
| 10 | ALADDIN CALENDAR | NC-17 |
...
1000 rows in set (0.00 sec)
在这个例子中,我们使用 CASE
将电影的等级转换为一个索引数字。然后使用 ORDER BY
按照这个数字进行排序。
可能您觉得 CASE
子句写起来很复杂,特别是列表值很多的时候。那么,请使用如下的 FIELD()
函数。
使用 FIELD()
函数实现自定义排序
对于上面实例中的 CASE
语句,我们可以如下的使用 FIELD()
代替。
sql
SELECT
*
FROM
film
ORDER BY FIELD(rating, 'G', 'PG', 'PG-13', 'R', 'NC-17');
输出结果与上面实例完全相同。
在本例中,我们使用 FIELD(rating, 'G', 'PG', 'PG-13', 'R', 'NC-17')
作为 ORDER BY
排序的表达式。其中 FIELD(value, value1, value2, ...)
函数返回 value
在 value1, value2, ...
列表中的位置。
ORDER BY 和 NULL
在 MySQL 中的升序排序中, NULL
值出现在非 NULL 值之前。
我们下面的实例使用以下临时数据作为演示:
sql
SELECT 'A' AS v
UNION ALL
SELECT 'B' AS v
UNION ALL
SELECT NULL AS v
UNION ALL
SELECT 0 AS v
UNION ALL
SELECT 1 AS v;
sql
+------+
| v |
+------+
| A |
| B |
| NULL |
| 0 |
| 1 |
+------+
当我们使用 ORDER BY
子句升序 ASC
排序时, NULL
值排在非 NULL
值的前面。如下:
sql
SELECT *
FROM (
SELECT 'A' AS v
UNION ALL
SELECT 'B' AS v
UNION ALL
SELECT NULL AS v
UNION ALL
SELECT 0 AS v
UNION ALL
SELECT 1 AS v
) t
ORDER BY v;
sql
+------+
| v |
+------+
| NULL |
| 0 |
| 1 |
| A |
| B |
+------+
当我们使用 ORDER BY
子句降序 DESC
排序时, NULL
值排在非 NULL
值的后面。如下:
sql
SELECT *
FROM (
SELECT 'A' AS v
UNION ALL
SELECT 'B' AS v
UNION ALL
SELECT NULL AS v
UNION ALL
SELECT 0 AS v
UNION ALL
SELECT 1 AS v
) t
ORDER BY v DESC;
sql
+------+
| v |
+------+
| B |
| A |
| 1 |
| 0 |
| NULL |
+------+
总结
使用 ORDER BY
子句按一列或多列对结果集进行排序。
使用 ASC
选项对结果集进行升序排序,使用DESC
选项对结果集进行降序排序。
使用 FIELD()
函数或者 CASE
子句可按照自定义的序列排序。
升序排序时, NULL
在非 NULL 值之前;降序排序时,NULL
在非 NULL 值之后。
MySQL LIMIT 用法与实例
在 MySQL 中,我们使用 LIMIT
子句来限定 SELECT
语句返回的行的数量。
MySQL LIMIT 语法
该 LIMIT
子句可用于限制 SELECT
语句返回的行数。 LIMIT
接受一个或两个非负数正数作为参数。 LIMIT
子句的语法如下:
ini
LIMIT [offset,] row_count;
或者
sql
LIMIT row_count OFFSET offset;
上述两种语法的结果是等效的,只是写法略有不同。
offset
指定要返回的第一行的偏移量。偏移量是相对于未使用 LIMIT
语句时的原始结果集而言的。offset
可理解为在原始结果集的基础上跳过的行数。
row_count
执行要返回的最大行数。
offset
是可选的。当未指定 offset
时,默认的值 offset
为 0
。
LIMIT
一般位于 SELECT
语句的最后。
例如:
LIMIT 5
最多返回 5 个记录行,等效于 LIMIT 0 5
LIMIT 2 5
在原始结果集中,跳过 2 个记录行,并从 第 3 个记录行开始,最多返回 5 个记录行。
LIMIT 和 ORDER BY 子句
在 SELECT
语句中, LIMIT
子句经常和 ORDER BY
子句结合使用。比如在下面的场景中:
ERP 中显示销售额最高的 5 个销售员
网站上的点击率最高的 10 个文章
论坛中一个月内最活跃的 10 个会员
博客网站的文章分页列表
在这些场景中,我们都需要先按照某个规则排序,然后使用取出指定数量的记录行。
sql
SELECT
select_expression, ...
FROM
table_name
ORDER BY
sort_expression, ...
LIMIT [offset,] row_count;
在此 SELECT
语句中, MySQL 会先按照 ORDER BY
指定的规则对排序结果集,再返回 LIMIT
子句指定的行数返回结果集。
MySQL LIMIT 实例
在以下实例中,我们使用 Sakila 示例数据库中的 film
表进行演示。
查询片长最长的 10 部影片
以下 SQL 语句返回 film
表中等级为 G 的片长最长的 10 部影片。
sql
SELECT film_id, title, rating,length FROM film WHERE rating='G' ORDER BY length DESC LIMIT 10;
sql
+---------+--------------------+--------+
| film_id | title | length |
+---------+--------------------+--------+
| 212 | DARN FORRESTER | 185 |
| 182 | CONTROL ANTHEM | 185 |
| 609 | MUSCLE BRIGHT | 185 |
| 597 | MOONWALKER FOOL | 184 |
| 128 | CATCH AMISTAD | 183 |
| 996 | YOUNG LANGUAGE | 183 |
| 50 | BAKED CLEOPATRA | 182 |
| 467 | INTRIGUE WORST | 181 |
| 510 | LAWLESS VISION | 181 |
| 612 | MUSSOLINI SPOILERS | 180 |
+---------+--------------------+--------+
查询片长最短的 10 部影片
以下 SQL 语句返回 film
表中等级为 G
的片长最短的 10 部影片。
vbnet
SELECT film_id, title, rating,length FROM film WHERE rating="G" ORDER BY length LIMIT 10;
diff
+---------+---------------------+--------+
| film_id | title | length |
+---------+---------------------+--------+
| 247 | DOWNHILL ENOUGH | 47 |
| 237 | DIVORCE SHINING | 47 |
| 2 | ACE GOLDFINGER | 48 |
| 575 | MIDSUMMER GROUNDHOG | 48 |
| 430 | HOOK CHARIOTS | 49 |
| 83 | BLUES INSTINCT | 50 |
| 292 | EXCITEMENT EVE | 51 |
| 402 | HARPER DYING | 52 |
| 794 | SIDE ARK | 52 |
| 542 | LUST LOCK | 52 |
+---------+---------------------+--------+
使用 LIMIT 子句进行分页
LIMIT 的一个很重要的应用就是分页查询。对于一些大型的数据表来说,分页查询能很好的减少数据库的消耗和提高用户体验。
film
表中共有 1000 行数据,这个我们可以通过以下的 COUNT(*)
查询得知。
sql
mysql> SELECT COUNT(*) FROM film;
+----------+
| COUNT(*) |
+----------+
| 1000 |
+----------+
1 row in set (0.00 sec)
如果没有分页查询,所有数据都显示在页面上,会引入以下的问题:
增加数据库的性能消耗
传输过程中的数据量增加
用户的体验不好,太多的数据对让用户眼花缭乱。
如果我们引入分页,每页显示 10 行数据,那么 1000 行数据需要 100 (1000 / 10
) 页就能显示完全。
查询第一个页只需限制最多行数为 10 行数据即可,整个 SQL 如下:
sql
SELECT film_id, title FROM film LIMIT 10;
diff
+---------+------------------+
| film_id | title |
+---------+------------------+
| 1 | ACADEMY DINOSAUR |
| 2 | ACE GOLDFINGER |
| 3 | ADAPTATION HOLES |
| 4 | AFFAIR PREJUDICE |
| 5 | AFRICAN EGG |
| 6 | AGENT TRUMAN |
| 7 | AIRPLANE SIERRA |
| 8 | AIRPORT POLLOCK |
| 9 | ALABAMA DEVIL |
| 10 | ALADDIN CALENDAR |
+---------+------------------+
要查询第二页需要先跳过第一页的 10 行数据并限制最多行数为 10 行数据,整个 SQL 如下:
sql
SELECT film_id,title FROM film LIMIT 10,10;
diff
+---------+------------------+
| film_id | title |
+---------+------------------+
| 1 | ACADEMY DINOSAUR |
| 2 | ACE GOLDFINGER |
| 3 | ADAPTATION HOLES |
| 4 | AFFAIR PREJUDICE |
| 5 | AFRICAN EGG |
| 6 | AGENT TRUMAN |
| 7 | AIRPLANE SIERRA |
| 8 | AIRPORT POLLOCK |
| 9 | ALABAMA DEVIL |
| 10 | ALADDIN CALENDAR |
+---------+------------------+
同样,最后一页的 SQL 语句如下:
sql
SELECT film_id, title FROM film LIMIT 990, 10;
总结
LIMIT
子句限定查询返回的行数。
LIMIT [offset,] row_count;
和 LIMIT row_count OFFSET offset;
是等效的。
offset
指定需要跳过的行数。当 offset
为 0
时,可以省略。
row_count
指定返回的最大行数。当 row_count
为 0
时,返回结果集为空。
LIMIT
子句经常用于分页查询。
LIMIT
子句经常结合 ORDER BY
查询排名列表。
MySQL DISTINCT 用法和实例
当使用 SELECT
查询数据时,我们可能会得到一些重复的行。比如演员表中有很多重复的姓氏。如果想得到一个唯一的、没有重复记录的结果集,就需要用到 DISTINCT
关键字。
MySQL DISTINCT 用法
在 SELECT
语句中使用 DISTINCT
关键字会返回一个没有重复记录行的结果集。 DISTINCT
的用法如下:
sql
SELECT DISTINCT
columns_list
FROM
table_name
DISTINCT
关键字位于 SELECT
关键字的后面。
columns_list
指定要查询的字段列表,也是 DISTINCT
评估记录行是否唯一的字段。
columns_list
可以是一个字段,也可以是多个字段。
columns_list
也可以是 *
。
MySQL DISTINCT 实例
在以下实例中,我们使用 Sakila 示例数据库中的 actor
表进行演示。
DISTINCT 单个字段
姓氏重复是一个很正常的现象。我们先使用 SELECT
语句检索以下 actor
表中的姓氏。
sql
SELECT last_name FROM actor;
sql
+--------------+
| last_name |
+--------------+
| AKROYD |
| AKROYD |
| AKROYD |
| ALLEN |
| ALLEN |
...
| ZELLWEGER |
| ZELLWEGER |
+--------------+
200 rows in set (0.01 sec)
然后我们使用 DISTINCT
删除重复的数据,返回唯一的姓氏列表:
sql
SELECT DISTINCT last_name FROM actor;
sql
+--------------+
| last_name |
+--------------+
| AKROYD |
| ALLEN |
| ASTAIRE |
| BACALL |
| BAILEY |
...
| ZELLWEGER |
+--------------+
121 rows in set (0.00 sec)
我们可以看到输出的结果集中已经没有了重复数据。结果集的行数也从原来的 200 行变成了 121 行。
DISTINCT 多个字段
DISTINCT
还可以清除多个字段的重复值。当指定多个字段值时, DISTINCT
使用多个字段组合确定记录行的唯一性。
现实中,我们不但存在姓氏重复的情况,名字也可能重复。我们检索 actor
表中的名字和姓氏:
sql
SELECT last_name, first_name FROM actor;
sql
+--------------+-------------+
| last_name | first_name |
+--------------+-------------+
| GUINESS | PENELOPE |
| WAHLBERG | NICK |
| CHASE | ED |
| DAVIS | JENNIFER |
| LOLLOBRIGIDA | JOHNNY |
| NICHOLSON | BETTE |
...
| TEMPLE | THORA |
+--------------+-------------+
200 rows in set (0.01 sec)
然后我们使用 DISTINCT
删除重复的数据:
sql
SELECT DISTINCT last_name,first_name FROM actor;
sql
+--------------+-------------+
| last_name | first_name |
+--------------+-------------+
| GUINESS | PENELOPE |
| WAHLBERG | NICK |
| CHASE | ED |
| DAVIS | JENNIFER |
| LOLLOBRIGIDA | JOHNNY |
...
| TEMPLE | THORA |
+--------------+-------------+
199 rows in set (0.01 sec)
我们可以看到输出结果集的行数也从原来的 200 行变成了 199 行。这说明原来的结果集中有一行重复记录。我们通过以下的分组汇总语句也能证明这一点:
sql
SELECT last_name, first_name, COUNT(*)
FROM actor
GROUP BY last_name , first_name
HAVING COUNT(*) > 1;
sql
+-----------+------------+----------+
| last_name | first_name | COUNT(*) |
+-----------+------------+----------+
| DAVIS | SUSAN | 2 |
+-----------+------------+----------+
1 row in set (0.00 sec)
注意: 上面的 SQL 语句使用了 GROUP BY
, HAVING
, COUNT()
对数据进行了分组汇总。
DISTINCT 与 NULL
当 DISTINCT
遇到 NULL
值时,只保留一个 NULL
值。因为 DISTINCT
认为所有的 NULL
值都是相同的,这与字段的类型无关。
例如下面的 SQL 使用返回多行 NULL
记录:
sql
SELECT *
FROM (
SELECT NULL
UNION ALL
SELECT NULL
UNION ALL
SELECT NULL
) t;
sql
+------------+
| NULL |
+------------+
| NULL |
| NULL |
| NULL |
+------------+
3 rows in set (0.00 sec)
当使用 DISTINCT
之后:
sql
SELECT DISTINCT *
FROM (
SELECT NULL
UNION ALL
SELECT NULL
UNION ALL
SELECT NULL
) t;
sql
+------------+
| NULL |
+------------+
| NULL |
+------------+
1 row in set (0.00 sec)
本例使用 UNION
关键字模拟包含多个 NULL
记录值的记录集。
总结
DISTINCT
清除 SELECT
结果集中的重复记录行。
DISTINCT
可以用一个字段或者多个字段,也可以用 *
。
DISTINCT
多个 NULL
值只保留一个 NULL
值。