当前位置: 首页 > news >正文

板凳-------Mysql cookbook学习 (八--2)

3.10 在用户程序中使用null作为比较参数

mysql> select * from taxpayer;
+---------+--------+
| name    | id     |
+---------+--------+
| bernina | 198-48 |
| bertha  | NULL   |
| ben     | NULL   |
| NULL    | 475-83 |
| baidu   | 111+55 |
+---------+--------+
5 rows in set (0.00 sec)

3.11 结果集排序

mysql> select * from mail where size > 100000 order by size;
+---------------------+---------+---------+---------+---------+---------+
| t                   | srcuser | srchost | dstuser | dsthost | size    |
+---------------------+---------+---------+---------+---------+---------+
| 2006-05-12 12:48:13 | tricia  | mars    | gene    | venus   |  194925 |
| 2006-05-15 10:25:52 | gene    | mars    | tricia  | saturn  |  998532 |
| 2006-05-14 17:03:01 | tricia  | saturn  | phil    | venus   | 2394482 |
+---------------------+---------+---------+---------+---------+---------+
3 rows in set (0.00 sec)
mysql> select * from mail where dstuser = 'tricia'-> order by srchost, srcuser;
+---------------------+---------+---------+---------+---------+--------+
| t                   | srcuser | srchost | dstuser | dsthost | size   |
+---------------------+---------+---------+---------+---------+--------+
| 2006-05-15 10:25:52 | gene    | mars    | tricia  | saturn  | 998532 |
| 2006-05-14 11:52:17 | phil    | mars    | tricia  | saturn  |   5781 |
| 2006-05-17 12:49:23 | phil    | mars    | tricia  | saturn  |    873 |
| 2006-05-11 10:15:08 | barb    | saturn  | tricia  | mars    |  58274 |
| 2006-05-13 13:59:18 | barb    | saturn  | tricia  | venus   |    271 |
+---------------------+---------+---------+---------+---------+--------+
5 rows in set (0.00 sec)
mysql> select * from mail where size > 50000 order by size desc;
+---------------------+---------+---------+---------+---------+---------+
| t                   | srcuser | srchost | dstuser | dsthost | size    |
+---------------------+---------+---------+---------+---------+---------+
| 2006-05-14 17:03:01 | tricia  | saturn  | phil    | venus   | 2394482 |
| 2006-05-15 10:25:52 | gene    | mars    | tricia  | saturn  |  998532 |
| 2006-05-12 12:48:13 | tricia  | mars    | gene    | venus   |  194925 |
| 2006-05-14 14:42:21 | barb    | venus   | barb    | venus   |   98151 |
| 2006-05-11 10:15:08 | barb    | saturn  | tricia  | mars    |   58274 |
+---------------------+---------+---------+---------+---------+---------+
5 rows in set (0.00 sec)

3.12 使用视图来简化查询

mysql> select-> date_format(t, '%M %e %Y') as date_sent,-> concat(srcuser, '@', srchost) as sender,-> concat(dstuser, '@', dsthost) as recipient,-> size from mail;
+-------------+---------------+---------------+---------+
| date_sent   | sender        | recipient     | size    |
+-------------+---------------+---------------+---------+
| May 11 2006 | barb@saturn   | tricia@mars   |   58274 |
| May 12 2006 | tricia@mars   | gene@venus    |  194925 |
| May 12 2006 | phil@mars     | phil@saturn   |    1048 |
| May 13 2006 | barb@saturn   | tricia@venus  |     271 |
| May 14 2006 | gene@venus    | barb@mars     |    2291 |
| May 14 2006 | phil@mars     | tricia@saturn |    5781 |
| May 14 2006 | barb@venus    | barb@venus    |   98151 |
| May 14 2006 | tricia@saturn | phil@venus    | 2394482 |
| May 15 2006 | gene@mars     | gene@saturn   |    3824 |
| May 15 2006 | phil@venus    | phil@venus    |     978 |
| May 15 2006 | gene@mars     | tricia@saturn |  998532 |
| May 15 2006 | gene@saturn   | gene@mars     |    3856 |
| May 16 2006 | gene@venus    | barb@mars     |     613 |
| May 16 2006 | phil@venus    | barb@venus    |   10294 |
| May 17 2006 | phil@mars     | tricia@saturn |     873 |
| May 19 2006 | gene@saturn   | gene@venus    |   23992 |
+-------------+---------------+---------------+---------+
16 rows in set (0.00 sec)
mysql> select * from mail_view;
+--------------+---------------+---------------+---------+
| date_sent    | sender        | recipient     | size    |
+--------------+---------------+---------------+---------+
| May 11, 2006 | barb@saturn   | tricia@mars   |   58274 |
| May 12, 2006 | tricia@mars   | gene@venus    |  194925 |
| May 12, 2006 | phil@mars     | phil@saturn   |    1048 |
| May 13, 2006 | barb@saturn   | tricia@venus  |     271 |
| May 14, 2006 | gene@venus    | barb@mars     |    2291 |
| May 14, 2006 | phil@mars     | tricia@saturn |    5781 |
| May 14, 2006 | barb@venus    | barb@venus    |   98151 |
| May 14, 2006 | tricia@saturn | phil@venus    | 2394482 |
| May 15, 2006 | gene@mars     | gene@saturn   |    3824 |
| May 15, 2006 | phil@venus    | phil@venus    |     978 |
| May 15, 2006 | gene@mars     | tricia@saturn |  998532 |
| May 15, 2006 | gene@saturn   | gene@mars     |    3856 |
| May 16, 2006 | gene@venus    | barb@mars     |     613 |
| May 16, 2006 | phil@venus    | barb@venus    |   10294 |
| May 17, 2006 | phil@mars     | tricia@saturn |     873 |
| May 19, 2006 | gene@saturn   | gene@venus    |   23992 |
+--------------+---------------+---------------+---------+
16 rows in set (0.00 sec)
mysql> select date_sent, sender, size from mail_view-> where size > 100000 order by size;
+--------------+---------------+---------+
| date_sent    | sender        | size    |
+--------------+---------------+---------+
| May 12, 2006 | tricia@mars   |  194925 |
| May 15, 2006 | gene@mars     |  998532 |
| May 14, 2006 | tricia@saturn | 2394482 |
+--------------+---------------+---------+
3 rows in set (0.00 sec)

3.13 多表查询

mysql> select * from profile;
+----+---------+------------+-------+-----------------------+------+
| id | name    | birth      | color | foods                 | cats |
+----+---------+------------+-------+-----------------------+------+
|  1 | Fred    | 1970-04-13 | black | lutefisk,fadge,pizza  |    1 |
|  2 | Mort    | 1969-09-30 | white | burrito,curry,eggroll |    3 |
|  3 | Brit    | 1957-12-01 | red   | burrito,curry,pizza   |    1 |
|  4 | Carl    | 1973-11-02 | red   | eggroll,pizza         |    4 |
|  5 | Sean    | 1963-07-04 | blue  | burrito,curry         |    5 |
|  6 | Alan    | 1965-02-14 | red   | curry,fadge           |    1 |
|  7 | Mara    | 1968-09-17 | green | lutefisk,fadge        |    1 |
|  8 | Shepard | 1975-09-02 | black | curry,pizza           |    2 |
|  9 | Dick    | 1952-08-20 | green | lutefisk,fadge        |    0 |
| 10 | Tony    | 1960-05-01 | white | burrito,pizza         |    0 |
| 11 | Alison  | 1973-01-12 | blue  | eggroll               |    4 |
| 12 | De'Mont | 1973-01-12 | NULL  | eggroll               |    4 |
| 13 | De'Mont | 1973-01-12 | NULL  | eggroll               |    4 |
| 14 | De'Mont | 1973-01-12 | NULL  | eggroll               |    4 |
| 15 | De'Mont | 1973-01-12 | NULL  | eggroll               |    4 |
| 16 | De'Mont | 1973-01-12 | NULL  | eggroll               |    4 |
| 17 | Amabel  | NULL       | NULL  | NULL                  | NULL |
| 18 | De'Mont | 1980-12-12 | NULL  | eggroll               |    8 |
| 19 | Juan    | NULL       | NULL  | NULL                  | NULL |
+----+---------+------------+-------+-----------------------+------+
19 rows in set (0.00 sec)
mysql> select * from profile_contact order by profile_id, service;
+------------+---------+---------------+
| profile_id | service | contact_name  |
+------------+---------+---------------+
|          1 | AIM     | user1-aimid   |
|          1 | MSN     | user1-msnid   |
|          2 | AIM     | user2-aimid   |
|          2 | MSN     | user2-msnid   |
|          2 | Yahoo   | user2-yahooid |
|          4 | Yahoo   | user4-yahooid |
+------------+---------+---------------+

6 rows in set (0.00 sec)

mysql> select id, name, service, contact_name-> from profile inner join profile_contact on id = profile_id;
+----+------+---------+---------------+
| id | name | service | contact_name  |
+----+------+---------+---------------+
|  1 | Fred | AIM     | user1-aimid   |
|  1 | Fred | MSN     | user1-msnid   |
|  2 | Mort | AIM     | user2-aimid   |
|  2 | Mort | MSN     | user2-msnid   |
|  2 | Mort | Yahoo   | user2-yahooid |
|  4 | Carl | Yahoo   | user4-yahooid |
+----+------+---------+---------------+
6 rows in set (0.00 sec)
mysql> select * from  profile_contact-> where profile_id = (select id from profile where name = 'Mort');
+------------+---------+---------------+
| profile_id | service | contact_name  |
+------------+---------+---------------+
|          2 | AIM     | user2-aimid   |
|          2 | MSN     | user2-msnid   |
|          2 | Yahoo   | user2-yahooid |
+------------+---------+---------------+

3 rows in set (0.00 sec)

3.14 从查询结果集头或尾取出部分行

mysql> select * from profile limit 1;
+----+------+------------+-------+----------------------+------+
| id | name | birth      | color | foods                | cats |
+----+------+------------+-------+----------------------+------+
|  1 | Fred | 1970-04-13 | black | lutefisk,fadge,pizza |    1 |
+----+------+------------+-------+----------------------+------+

1 row in set (0.00 sec)

mysql> select * from profile limit 10;
+----+---------+------------+-------+-----------------------+------+
| id | name    | birth      | color | foods                 | cats |
+----+---------+------------+-------+-----------------------+------+
|  1 | Fred    | 1970-04-13 | black | lutefisk,fadge,pizza  |    1 |
|  2 | Mort    | 1969-09-30 | white | burrito,curry,eggroll |    3 |
|  3 | Brit    | 1957-12-01 | red   | burrito,curry,pizza   |    1 |
|  4 | Carl    | 1973-11-02 | red   | eggroll,pizza         |    4 |
|  5 | Sean    | 1963-07-04 | blue  | burrito,curry         |    5 |
|  6 | Alan    | 1965-02-14 | red   | curry,fadge           |    1 |
|  7 | Mara    | 1968-09-17 | green | lutefisk,fadge        |    1 |
|  8 | Shepard | 1975-09-02 | black | curry,pizza           |    2 |
|  9 | Dick    | 1952-08-20 | green | lutefisk,fadge        |    0 |
| 10 | Tony    | 1960-05-01 | white | burrito,pizza         |    0 |
+----+---------+------------+-------+-----------------------+------+
10 rows in set (0.00 sec)

首先使用order by 对查询结果进行排序,再使用limit从查询结果中取出最大或最小特征的行。

mysql> select * from profile order by birth limit 1;
+----+--------+-------+-------+-------+------+
| id | name   | birth | color | foods | cats |
+----+--------+-------+-------+-------+------+
| 17 | Amabel | NULL  | NULL  | NULL  | NULL |
+----+--------+-------+-------+-------+------+
1 row in set (0.00 sec)mysql> select * from profile order by birth desc limit 1;
+----+---------+------------+-------+---------+------+
| id | name    | birth      | color | foods   | cats |
+----+---------+------------+-------+---------+------+
| 18 | De'Mont | 1980-12-12 | NULL  | eggroll |    8 |
+----+---------+------------+-------+---------+------+
1 row in set (0.00 sec)mysql> select name, date_format(birth, '%m-%d') as birthday-> from profile order by birthday limit 1;
+------+----------+
| name | birthday |
+------+----------+
| Juan | NULL     |
+------+----------+
1 row in set (0.00 sec)

3.15 在结果集中间选取部分行 185/951 Thursday, May 29, 2025

Select 	from:从哪些表中筛选
on:关联多表查询时,去除笛卡尔积
where:从表中筛选的条件
group by:分组依据
having:在统计结果中再次筛选
order by:排序
limit:分页

以下是对 SQL 查询执行逻辑的专业描述,并附 MySQL 可执行示例:

一、FROM 阶段(表关联处理)
1. 笛卡尔积生成(CROSS JOIN)
原理:生成所有表行的组合。若表 A 有m行,表 B 有n行,则结果包含m×n行。
示例表结构:
sql
CREATE TABLE A (id INT PRIMARY KEY, name VARCHAR(10));
CREATE TABLE B (id INT PRIMARY KEY, value INT);
INSERT INTO A VALUES (1,'Alice'), (2,'Bob');
INSERT INTO B VALUES (1,100), (2,200);执行逻辑:
sql
SELECT * FROM A CROSS JOIN B; -- 生成4行结果(2×2)结果:
plaintext
| id | name  | id | value |
|----|-------|----|-------|
| 1  | Alice | 1  | 100   |
| 1  | Alice | 2  | 200   |
| 2  | Bob   | 1  | 100   |
| 2  | Bob   | 2  | 200   |2. ON 条件筛选
原理:通过ON子句过滤无效行。
示例:
sql
SELECT * FROM A JOIN B ON A.id = B.id; -- 仅保留id匹配的行结果:
plaintext
| id | name  | id | value |
|----|-------|----|-------|
| 1  | Alice | 1  | 100   |
| 2  | Bob   | 2  | 200   |3. 外部行添加(外连接)
左连接示例:
sql
SELECT * FROM A LEFT JOIN B ON A.id = B.id; -- 保留A的未匹配行结果(若 B 中无匹配行,B 的字段为NULL):
plaintext
| id | name  | id | value |
|----|-------|----|-------|
| 1  | Alice | 1  | 100   |
| 2  | Bob   | 2  | 200   |
| 3  | Carol | NULL| NULL  | -- 假设A新增行(3,'Carol')二、WHERE 阶段(行过滤)
原理:基于FROM结果筛选行。
示例:
sql
SELECT * FROM A JOIN B ON A.id = B.id WHERE B.value > 150;结果:
plaintext
| id | name | id | value |
|----|------|----|-------|
| 2  | Bob  | 2  | 200   |三、GROUP BY 阶段(分组操作)
原理:按指定列分组,非分组列需用聚合函数。
示例:
sql
SELECT A.name, SUM(B.value) 
FROM A JOIN B ON A.id = B.id 
GROUP BY A.name;结果:
plaintext
| name  | SUM(B.value) |
|-------|-------------|
| Alice | 100         |
| Bob   | 200         |四、HAVING 阶段(分组过滤)
原理:筛选符合条件的分组。
示例:
sql
SELECT A.name, SUM(B.value) 
FROM A JOIN B ON A.id = B.id 
GROUP BY A.name 
HAVING SUM(B.value) > 150;结果:
plaintext
| name | SUM(B.value) |
|------|-------------|
| Bob  | 200         |五、SELECT 阶段(字段提取)
原理:提取指定列或表达式。
示例:
sql
SELECT A.name AS user, B.value*1.1 AS adjusted_value 
FROM A JOIN B ON A.id = B.id;结果:
plaintext
| user  | adjusted_value |
|-------|----------------|
| Alice | 110.0          |
| Bob   | 220.0          |六、DISTINCT 阶段(去重)
原理:移除完全重复的行。
示例:
sql
SELECT DISTINCT value FROM B;结果(若 B 中有重复值100,100,200):
plaintext
| value |
|-------|
| 100   |
| 200   |七、ORDER BY 阶段(排序)
原理:按指定列排序。
示例:
sql
SELECT * FROM B ORDER BY value DESC;结果:
plaintext
| id | value |
|----|-------|
| 2  | 200   |
| 1  | 100   |八、LIMIT 阶段(分页截取)
原理:限制返回行数。
示例:
sql
SELECT * FROM B LIMIT 1; -- 返回第1行结果:
plaintext
| id | value |
|----|-------|
| 1  | 100   |完整执行顺序示例
查询语句:
sql
SELECT A.name, SUM(B.value) AS total
FROM A
LEFT JOIN B ON A.id = B.id
WHERE A.id > 0
GROUP BY A.name
HAVING total > 50
ORDER BY total DESC
LIMIT 1;
执行步骤:
FROM+ON:左连接 A 和 B,保留 A 的未匹配行。
WHERE:过滤出 A.id>0 的行(若所有 A.id>0,则无影响)。
GROUP BY:按 A.name 分组。
HAVING:筛选总和 > 50 的分组。
SELECT:提取 A.name 和 SUM (B.value)ORDER BY:按总和降序排列。
LIMIT:取第 1 行。
结果(假设数据如前):
plaintext
| name | total |
|------|-------|
| Bob  | 200   |
执行顺序总结
plaintext
FROMON → 外连接处理 → WHEREGROUP BYHAVINGSELECTDISTINCTORDER BYLIMIT

二、SELECT 语句的底层执行逻辑(以 MySQL 为例)

1. 执行顺序与阶段解析
SQL 语句的书写顺序(如 SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ... LIMIT)与实际执行顺序不同。以下是按执行顺序排列的核心阶段(附实例说明):
执行阶段	说明	实例(MySQL 可执行)
1. FROM 阶段	- 解析表名,生成初始结果集(多张表时先计算笛卡尔积)
- 处理 JOIN 逻辑(内连接 / 外连接)	sql<br>SELECT *<br>FROM orders o<br>LEFT JOIN users u ON o.user_id = u.id; -- 先执行 FROM + ON 条件<br>
2. ON 过滤	-JOIN 生成的临时表应用 ON 条件(仅在 JOIN 时生效)	同上,ON o.user_id = u.id 在此阶段执行过滤。
3. 添加外部行	- 处理外连接(左 // 全连接),补充主表中不满足 ON 条件的行(值为 NULL)	左连接时,users 表中无匹配的 orders 行会保留,对应字段为 NULL4. WHERE 过滤	-FROM 阶段生成的临时表应用 WHERE 条件(行级过滤)	sql<br>SELECT *<br>FROM orders o<br>LEFT JOIN users u ON o.user_id = u.id<br>WHERE o.order_date > '2023-01-01'; -- WHERE 在此阶段执行<br>
5. GROUP BY 分组	- 按指定字段分组,生成分组后的临时表(行合并)	sql<br>SELECT user_id, SUM(amount) AS total<br>FROM orders<br>WHERE order_date > '2023-01-01'<br>GROUP BY user_id; -- 分组在此阶段执行<br>
6. HAVING 过滤	- 对分组后的结果应用 HAVING 条件(分组级过滤)	sql<br>SELECT user_id, SUM(amount) AS total<br>FROM orders<br>WHERE order_date > '2023-01-01'<br>GROUP BY user_id<br>HAVING total > 1000; -- HAVING 在此阶段执行<br>
7. SELECT 投影	- 计算 SELECT 中的表达式,提取目标字段(列选择与计算)	sql<br>SELECT user_id, CONCAT('用户', user_id) AS user_label -- CONCAT 在此阶段计算<br>FROM users;<br>
8. DISTINCT 去重	- 过滤重复行,仅保留唯一记录	sql<br>SELECT DISTINCT user_id<br>FROM orders;<br>
9. ORDER BY 排序	- 对结果集按指定字段排序(内存排序或文件排序)	sql<br>SELECT *<br>FROM orders<br>ORDER BY order_date DESC; -- 排序在此阶段执行<br>
10. LIMIT 分页	- 截取指定行数的结果(最后一步执行)	sql<br>SELECT *<br>FROM orders<br>ORDER BY order_date DESC<br>LIMIT 10 OFFSET 20; -- LIMIT 在此阶段执行<br>
2. 关键逻辑说明
ONWHERE 的区别:
ON 用于 表关联时的条件过滤,发生在 FROM 阶段,可访问未过滤的原始表数据(如外连接中主表的 NULL 值)。
WHERE 用于 对最终临时表的行级过滤,发生在 FROMJOIN 之后,无法访问外连接中未匹配的 NULL 行(会被提前过滤)。
示例:
sql
-- 左连接中,ON 可保留主表 NULL 行,WHERE 会过滤 NULL 行  
SELECT *  
FROM orders o  
LEFT JOIN users u ON o.user_id = u.id AND u.status = 'active'; -- ON 条件中包含过滤逻辑  
-- 等价于:先左连接,再保留 u.status='active' 的匹配行,主表 NULL 行保留  SELECT *  
FROM orders o  
LEFT JOIN users u ON o.user_id = u.id  
WHERE u.status = 'active'; -- WHERE 会过滤掉 u 为 NULL 的行(主表无匹配的记录被删除)  分组与聚合的逻辑:
GROUP BY 会将相同分组字段的行合并为一行,并通过聚合函数(如 SUM、COUNT)计算分组值。
HAVING 必须依赖 GROUP BY,用于过滤分组后的结果(例如 “筛选总金额超过 1000 的用户分组”)。
性能优化关键点:
尽早过滤:将过滤条件(如 WHERE)放在靠前的阶段(避免处理无效数据)。
避免 SELECT *:仅选择需要的字段,减少数据传输和计算量。
合理使用索引:在 JOINWHEREORDER BY 字段上创建索引,加速过滤和排序。
三、总结
SELECT 语句的底层逻辑是 「按阶段逐步处理数据,从表关联到最终结果集生成」,执行顺序与书写顺序不同。理解这一逻辑有助于优化 SQL 性能,避免逻辑错误(如误用 ONWHERE)。实际开发中,可通过 MySQL 的 EXPLAIN 语句分析执行计划,验证优化效果。
mysql> select count(*) from profile;
+----------+
| count(*) |
+----------+
|       19 |
+----------+
1 row in set (0.02 sec)mysql> select sql_calc_found_rows * from profile order by name limit 5;
+----+--------+------------+-------+---------------------+------+
| id | name   | birth      | color | foods               | cats |
+----+--------+------------+-------+---------------------+------+
|  6 | Alan   | 1965-02-14 | red   | curry,fadge         |    1 |
| 11 | Alison | 1973-01-12 | blue  | eggroll             |    4 |
| 17 | Amabel | NULL       | NULL  | NULL                | NULL |
|  3 | Brit   | 1957-12-01 | red   | burrito,curry,pizza |    1 |
|  4 | Carl   | 1973-11-02 | red   | eggroll,pizza       |    4 |
+----+--------+------------+-------+---------------------+------+
5 rows in set, 1 warning (0.00 sec)mysql> select found_rows();
+--------------+
| found_rows() |
+--------------+
|           19 |
+--------------+
1 row in set, 1 warning (0.01 sec)3.16 选择合适的limit参数
mysql> select sql_calc_found_rows * from profile order by name limit 5;
+----+--------+------------+-------+---------------------+------+
| id | name   | birth      | color | foods               | cats |
+----+--------+------------+-------+---------------------+------+
|  6 | Alan   | 1965-02-14 | red   | curry,fadge         |    1 |
| 11 | Alison | 1973-01-12 | blue  | eggroll             |    4 |
| 17 | Amabel | NULL       | NULL  | NULL                | NULL |
|  3 | Brit   | 1957-12-01 | red   | burrito,curry,pizza |    1 |
|  4 | Carl   | 1973-11-02 | red   | eggroll,pizza       |    4 |
+----+--------+------------+-------+---------------------+------+
5 rows in set, 1 warning (0.00 sec)mysql> select found_rows();
+--------------+
| found_rows() |
+--------------+
|           19 |
+--------------+
1 row in set, 1 warning (0.01 sec)mysql> select name, wins from al_winner-> order by wins desc, name;
+----------------+------+
| name           | wins |
+----------------+------+
| Mulder, Mark   |   21 |
| Clemens, Roger |   20 |
| Moyer, Jamie   |   20 |
| Garcia, Freddy |   18 |
| Hudson, Tim    |   18 |
| Abbott, Paul   |   17 |
| Mays, Joe      |   17 |
| Mussina, Mike  |   17 |
| Sabathia, C.C. |   17 |
| Zito, Barry    |   17 |
| Buehrle, Mark  |   16 |
| Milton, Eric   |   15 |
| Pettitte, Andy |   15 |
| Radke, Brad    |   15 |
| Sele, Aaron    |   15 |
+----------------+------+
15 rows in set (0.02 sec)mysql> select name, wins from al_winner-> order by wins desc, name-> limit 5;
+----------------+------+
| name           | wins |
+----------------+------+
| Mulder, Mark   |   21 |
| Clemens, Roger |   20 |
| Moyer, Jamie   |   20 |
| Garcia, Freddy |   18 |
| Hudson, Tim    |   18 |
+----------------+------+
5 rows in set (0.00 sec)mysql> select name, wins from al_winner-> order by wins desc, name-> limit 3, 1;
+----------------+------+
| name           | wins |
+----------------+------+
| Garcia, Freddy |   18 |
+----------------+------+
1 row in set (0.00 sec)mysql> select name, wins from al_winner-> where wins >= 18-> order by wins desc, name;
+----------------+------+
| name           | wins |
+----------------+------+
| Mulder, Mark   |   21 |
| Clemens, Roger |   20 |
| Moyer, Jamie   |   20 |
| Garcia, Freddy |   18 |
| Hudson, Tim    |   18 |
+----------------+------+
5 rows in set (0.00 sec)mysql> SELECT name, wins-> FROM al_winner-> WHERE wins >= (->     SELECT wins->     FROM al_winner->     ORDER BY wins DESC, name->     LIMIT 3, 1  -- 英文逗号+英文括号-> )-> ORDER BY wins DESC, name;
+----------------+------+
| name           | wins |
+----------------+------+
| Mulder, Mark   |   21 |
| Clemens, Roger |   20 |
| Moyer, Jamie   |   20 |
| Garcia, Freddy |   18 |
| Hudson, Tim    |   18 |
+----------------+------+
5 rows in set (0.00 sec)mysql> SELECT DISTINCT wins-> FROM al_winner-> ORDER BY wins DESC  -- 仅保留 wins 列-> LIMIT 3, 1;
+------+
| wins |
+------+
|   17 |
+------+
1 row in set (0.00 sec)mysql> SELECT wins-> FROM (->     SELECT wins,->            DENSE_RANK() OVER (ORDER BY wins DESC) AS rnk->     FROM al_winner-> ) t-> WHERE rnk = 4  -- 直接获取第4名的 wins-> LIMIT 1;
+------+
| wins |
+------+
|   17 |
+------+
1 row in set (0.01 sec)

总结
使用 DISTINCT 时:ORDER BY 必须仅引用 SELECT 列表中的列。
窗口函数:处理排名问题更直观,推荐优先使用(如 RANK()、DENSE_RANK())。
性能提示:若表数据量大,添加 INDEX(wins) 可加速排序和去重。

mysql> select name, wins from al_winner-> where wins >= 17-> order by wins desc, name;
+----------------+------+
| name           | wins |
+----------------+------+
| Mulder, Mark   |   21 |
| Clemens, Roger |   20 |
| Moyer, Jamie   |   20 |
| Garcia, Freddy |   18 |
| Hudson, Tim    |   18 |
| Abbott, Paul   |   17 |
| Mays, Joe      |   17 |
| Mussina, Mike  |   17 |
| Sabathia, C.C. |   17 |
| Zito, Barry    |   17 |
+----------------+------+
10 rows in set (0.00 sec)

原查询的问题在于子查询的 ORDER BY name 与 DISTINCT wins 不兼容。通过移除 name 解决了这个问题,同时确保主查询逻辑不变。
若你的 MySQL 版本支持窗口函数(8.0+),强烈推荐使用方案 2,它更健壮且易于理解。

mysql> SELECT name, wins-> FROM al_winner-> WHERE wins >= (->     SELECT DISTINCT wins        -- 仅选择 wins 列->     FROM al_winner->     ORDER BY wins DESC          -- 仅按 wins 排序->     LIMIT 3, 1                  -- 获取第4高的 wins 值-> )-> ORDER BY wins DESC, name;        -- 主查询排序正常
+----------------+------+
| name           | wins |
+----------------+------+
| Mulder, Mark   |   21 |
| Clemens, Roger |   20 |
| Moyer, Jamie   |   20 |
| Garcia, Freddy |   18 |
| Hudson, Tim    |   18 |
| Abbott, Paul   |   17 |
| Mays, Joe      |   17 |
| Mussina, Mike  |   17 |
| Sabathia, C.C. |   17 |
| Zito, Barry    |   17 |
+----------------+------+
10 rows in set (0.00 sec)mysql> SELECT name, wins-> FROM al_winner-> WHERE wins >= (->     SELECT wins->     FROM (->         SELECT DISTINCT wins,->                DENSE_RANK() OVER (ORDER BY wins DESC) AS rnk  -- 计算排名->         FROM al_winner->     ) t->     WHERE rnk = 4                -- 直接获取第4名的 wins-> );
+----------------+------+
| name           | wins |
+----------------+------+
| Abbott, Paul   |   17 |
| Clemens, Roger |   20 |
| Garcia, Freddy |   18 |
| Hudson, Tim    |   18 |
| Mays, Joe      |   17 |
| Moyer, Jamie   |   20 |
| Mulder, Mark   |   21 |
| Mussina, Mike  |   17 |
| Sabathia, C.C. |   17 |
| Zito, Barry    |   17 |
+----------------+------+
10 rows in set (0.00 sec)

3.17 当limit需要“错误”的排列顺序时做什么

mysql> select name, birth from profile order by birth desc limit 4;
+---------+------------+
| name    | birth      |
+---------+------------+
| De'Mont | 1980-12-12 |
| Shepard | 1975-09-02 |
| Carl    | 1973-11-02 |
| Alison  | 1973-01-12 |
+---------+------------+
4 rows in set (0.00 sec)mysql> select count(*) from profile;
+----------+
| count(*) |
+----------+
|       19 |
+----------+
1 row in set (0.00 sec)mysql> select name, birth from profile order by birth desc limit 6, 4;
+---------+------------+
| name    | birth      |
+---------+------------+
| De'Mont | 1973-01-12 |
| De'Mont | 1973-01-12 |
| De'Mont | 1973-01-12 |
| Fred    | 1970-04-13 |
+---------+------------+
4 rows in set (0.00 sec)mysql> select * from-> (select name, birth from profile order by birth desc limit 5) as t-> order by birth;
+---------+------------+
| name    | birth      |
+---------+------------+
| De'Mont | 1973-01-12 |
| Alison  | 1973-01-12 |
| Carl    | 1973-11-02 |
| Shepard | 1975-09-02 |
| De'Mont | 1980-12-12 |
+---------+------------+
5 rows in set (0.00 sec)

3.18 从表达式中计算limit值
步骤解释:
定义变量 @limit_val。
使用 CONCAT 构建 SQL 字符串,将变量值嵌入其中。
通过 PREPARE 和 EXECUTE 执行动态生成的 SQL,确保变量被正确解析为整数。

mysql> SET @limit_val = 5 + 5;
Query OK, 0 rows affected (0.00 sec)mysql> SET @sql = CONCAT('SELECT * FROM profile LIMIT ', @limit_val);
Query OK, 0 rows affected (0.00 sec)mysql>
mysql> PREPARE stmt FROM @sql;
Query OK, 0 rows affected (0.00 sec)
Statement preparedmysql> EXECUTE stmt;
+----+---------+------------+-------+-----------------------+------+
| id | name    | birth      | color | foods                 | cats |
+----+---------+------------+-------+-----------------------+------+
|  1 | Fred    | 1970-04-13 | black | lutefisk,fadge,pizza  |    1 |
|  2 | Mort    | 1969-09-30 | white | burrito,curry,eggroll |    3 |
|  3 | Brit    | 1957-12-01 | red   | burrito,curry,pizza   |    1 |
|  4 | Carl    | 1973-11-02 | red   | eggroll,pizza         |    4 |
|  5 | Sean    | 1963-07-04 | blue  | burrito,curry         |    5 |
|  6 | Alan    | 1965-02-14 | red   | curry,fadge           |    1 |
|  7 | Mara    | 1968-09-17 | green | lutefisk,fadge        |    1 |
|  8 | Shepard | 1975-09-02 | black | curry,pizza           |    2 |
|  9 | Dick    | 1952-08-20 | green | lutefisk,fadge        |    0 |
| 10 | Tony    | 1960-05-01 | white | burrito,pizza         |    0 |
+----+---------+------------+-------+-----------------------+------+
10 rows in set (0.00 sec)mysql> DEALLOCATE PREPARE stmt;

使用存储过程(简化动态查询)
若需要频繁执行类似查询,可创建存储过程:

mysql> DELIMITER $$
mysql> CREATE PROCEDURE GetProfile(IN limit_val INT)-> BEGIN->     SELECT * FROM profile LIMIT limit_val;-> END$$
Query OK, 0 rows affected (0.04 sec)mysql> DELIMITER ;
mysql>
mysql> -- 调用存储过程
mysql> CALL GetProfile(10);  -- 直接传入数值
+----+---------+------------+-------+-----------------------+------+
| id | name    | birth      | color | foods                 | cats |
+----+---------+------------+-------+-----------------------+------+
|  1 | Fred    | 1970-04-13 | black | lutefisk,fadge,pizza  |    1 |
|  2 | Mort    | 1969-09-30 | white | burrito,curry,eggroll |    3 |
|  3 | Brit    | 1957-12-01 | red   | burrito,curry,pizza   |    1 |
|  4 | Carl    | 1973-11-02 | red   | eggroll,pizza         |    4 |
|  5 | Sean    | 1963-07-04 | blue  | burrito,curry         |    5 |
|  6 | Alan    | 1965-02-14 | red   | curry,fadge           |    1 |
|  7 | Mara    | 1968-09-17 | green | lutefisk,fadge        |    1 |
|  8 | Shepard | 1975-09-02 | black | curry,pizza           |    2 |
|  9 | Dick    | 1952-08-20 | green | lutefisk,fadge        |    0 |
| 10 | Tony    | 1960-05-01 | white | burrito,pizza         |    0 |
+----+---------+------------+-------+-----------------------+------+
10 rows in set (0.01 sec)Query OK, 0 rows affected (0.21 sec)mysql> -- 或使用变量
mysql> SET @limit = 5 + 5;
Query OK, 0 rows affected (0.00 sec)mysql> CALL GetProfile(@limit);
+----+---------+------------+-------+-----------------------+------+
| id | name    | birth      | color | foods                 | cats |
+----+---------+------------+-------+-----------------------+------+
|  1 | Fred    | 1970-04-13 | black | lutefisk,fadge,pizza  |    1 |
|  2 | Mort    | 1969-09-30 | white | burrito,curry,eggroll |    3 |
|  3 | Brit    | 1957-12-01 | red   | burrito,curry,pizza   |    1 |
|  4 | Carl    | 1973-11-02 | red   | eggroll,pizza         |    4 |
|  5 | Sean    | 1963-07-04 | blue  | burrito,curry         |    5 |
|  6 | Alan    | 1965-02-14 | red   | curry,fadge           |    1 |
|  7 | Mara    | 1968-09-17 | green | lutefisk,fadge        |    1 |
|  8 | Shepard | 1975-09-02 | black | curry,pizza           |    2 |
|  9 | Dick    | 1952-08-20 | green | lutefisk,fadge        |    0 |
| 10 | Tony    | 1960-05-01 | white | burrito,pizza         |    0 |
+----+---------+------------+-------+-----------------------+------+
10 rows in set (0.00 sec)Query OK, 0 rows affected (0.06 sec)

如果你需要频繁执行类似查询,创建存储过程更简洁:

– 创建存储过程

DELIMITER $$
CREATE PROCEDURE GetProfile(IN limit_val INT)
BEGINSELECT * FROM profile LIMIT limit_val;
END$$
DELIMITER ;-- 调用存储过程
CALL GetProfile(10);  -- 直接传入数值
-- 或使用变量
SET @limit = 5 + 5;
CALL GetProfile(@limit);
-------------------------------------------------------------
使用 PREPARE 语句
若需要动态生成 LIMIT 参数,可结合 预处理语句(Prepared Statements):
优点:安全处理动态参数,避免 SQL 注入。
mysql> SET @skip = 5;
Query OK, 0 rows affected (0.00 sec)mysql> SET @show = 5;
Query OK, 0 rows affected (0.00 sec)mysql> SET @sql = CONCAT('SELECT * FROM profile LIMIT ', @skip, ', ', @show);
Query OK, 0 rows affected (0.00 sec)mysql>
mysql> PREPARE stmt FROM @sql;
Query OK, 0 rows affected (0.01 sec)
Statement preparedmysql> EXECUTE stmt;
+----+---------+------------+-------+----------------+------+
| id | name    | birth      | color | foods          | cats |
+----+---------+------------+-------+----------------+------+
|  6 | Alan    | 1965-02-14 | red   | curry,fadge    |    1 |
|  7 | Mara    | 1968-09-17 | green | lutefisk,fadge |    1 |
|  8 | Shepard | 1975-09-02 | black | curry,pizza    |    2 |
|  9 | Dick    | 1952-08-20 | green | lutefisk,fadge |    0 |
| 10 | Tony    | 1960-05-01 | white | burrito,pizza  |    0 |
+----+---------+------------+-------+----------------+------+
5 rows in set (0.00 sec)mysql> DEALLOCATE PREPARE stmt;
Query OK, 0 rows affected (0.00 sec)

相关文章:

  • Oracle OCP认证的技术定位怎么样?
  • Redis Stack常见拓展
  • 2024长春全国邀请赛CCPC
  • 【Oracle】DCL语言
  • 掌握STP技术:网络环路终结者实战
  • 输电线路的“智慧之眼”:全天候可视化监测如何赋能电网安全运维
  • 网络编程之网络编程预备知识
  • 【题解-洛谷】B4295 [蓝桥杯青少年组国赛 2022] 报数游戏
  • Qt Creator调用Python代码
  • linux 1.0.3
  • TCP/IP四层模型
  • CI/CD 持续集成、持续交付、持续部署
  • 微信小程序(uniapp)实现腾讯云 IM 消息撤回
  • Flink
  • MySQL 8主从同步实战指南:从原理到高可用架构落地
  • C# Renci.SshNet 登陆 suse配置一粒
  • Ubuntu 中安装 PostgreSQL 及常规操作指南
  • 【Phytium】飞腾FT2000/4 GPIO功能开发实例【待完成】
  • 快速了解 GO之接口解耦
  • 变频器从入门到精通
  • 税务局门户网站建设/一个平台怎么推广
  • wordpress搭建商城网站/淘宝指数查询官网
  • 国内html5网站/软文编辑器
  • wordpress免费 主题/河南平价的seo整站优化定制
  • 中央政府网站集约化建设/搜索引擎优化的流程
  • 网站域名登记证明/知乎怎么申请关键词推广