MySQL ORDER BY、 LIMIT和DISTINCT 用法和实例

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, ...) 函数返回 valuevalue1, 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 时,默认的值 offset0

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 指定需要跳过的行数。当 offset0 时,可以省略。

row_count 指定返回的最大行数。当 row_count0 时,返回结果集为空。

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 值。

相关推荐
8Qi83 分钟前
Redis哨兵模式(Sentinel)深度解析
java·数据库·redis·分布式·缓存·sentinel
数据库小组6 分钟前
从业务库到实时分析库,NineData 构建 MySQL 到 SelectDB 同步链路
数据库·mysql·数据库管理工具·数据同步·ninedata·数据库迁移·selectdb
CDN36012 分钟前
CDN HTTPS 证书配置失败?SSL 部署与域名绑定常见问题
数据库·https·ssl
无籽西瓜a13 分钟前
【西瓜带你学设计模式 | 第五期 - 建造者模式】建造者模式 —— 产品构建实现、优缺点与适用场景及模式区别
java·后端·设计模式·软件工程·建造者模式
Chengbei1119 分钟前
一次比较简单的360加固APP脱壳渗透
网络·数据库·web安全·网络安全·系统安全·网络攻击模型·安全架构
寒秋花开曾相惜20 分钟前
(学习笔记)3.9 异质的数据结构(3.9.1 结构)
c语言·网络·数据结构·数据库·笔记·学习
mcooiedo30 分钟前
mybatisPlus打印sql配置
数据库·sql
wudl556636 分钟前
MySQL 8.0.42 Docker 开发部署手册
数据库·mysql·docker
xhuiting40 分钟前
MySQL专题总结(四)—— 高可用
java·数据库·mysql
小江的记录本1 小时前
【Spring注解】Spring生态常见注解——面试高频考点总结
java·spring boot·后端·spring·面试·架构·mvc