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

相关推荐
Asthenia04121 分钟前
常用索引有哪些?联合索引使用时要注意什么?什么是最左匹配原则?联合索引(a, b, c),使用(b, c) 可以命中索引吗?(a, c) 呢?
后端
Asthenia041220 分钟前
Redis性能与优势/对比其他Key-Value存储/数据类型及底层结构/相同数据结构原因/对比Memcached优势/字符串最大容量/RDB与AOF分析
后端
橘猫云计算机设计1 小时前
基于Java的班级事务管理系统(源码+lw+部署文档+讲解),源码可白嫖!
java·开发语言·数据库·spring boot·微信小程序·小程序·毕业设计
多多*1 小时前
JavaEE企业级开发 延迟双删+版本号机制(乐观锁) 事务保证redis和mysql的数据一致性 示例
java·运维·数据库·redis·mysql·java-ee·wpf
计算机-秋大田1 小时前
基于Spring Boot的个性化商铺系统的设计与实现(LW+源码+讲解)
java·vue.js·spring boot·后端·课程设计
熬了夜的程序员1 小时前
Go 语言封装邮件发送功能
开发语言·后端·golang·log4j
uhakadotcom1 小时前
PostgreSQL 行级安全性(RLS)简介
后端·面试·github
酷爱码1 小时前
数据库索引相关的面试题以及答案
数据库
以待成追忆1 小时前
Scrapy——Redis空闲超时关闭扩展
数据库·redis·scrapy