这种方式也不少见:
SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.uid IS NULL;
LEFT JOIN 的工作原理是,当 s1.rank 处于最大值时,没有 s2.rank 具有更大的值,并且 s2 行的值将为 NULL。
但我想说的是,你的做法是最常见、最容易理解的做法,是的。
编辑:关于为什么有时速度较慢的问题:
此查询的性能取决于“编写的仔细程度”。以您的数据为例:
drop table if exists students;
CREATE TABLE students
(`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int)
;
INSERT INTO students
(`uid`, `last_name`, `first_name`, `dob`, `email`, `rank`, `grade`)
VALUES
(13428700000001, 'Smith', 'John', '1990-12-03', '...@gmail.com', 99, 4),
(13428721960000, 'Li', 'Kai Li', '1979-02-15', '...@miryclay.com', 12, 2),
(13428722180001, 'Zhang', 'Xi Xiong', '1993-11-09', '...@163.com', 5, 5),
(13428739950000, 'Zhou', 'Ji Hai', '1991-06-06', '...@msn.com', 234, 1),
(13428739950001, 'Pan', 'Yao', '1992-05-12', '...@email.com', 43, 2),
(13428740010001, 'Jin', 'Denny', '1994-06-02', '...@yahoo.com', 198, 3),
(13428740010002, 'Li', 'Fonzie', '1991-02-02', '...@hotmail.com', 75, 3),
(13428743370000, 'Ma', 'Haggar', '1991-08-16', '...@haggars.com', 47, 4),
(13428743590001, 'Ren', 'Jenny', '1990-03-29', '...@email.com', 5, 2),
(13428774040000, 'Chen', 'Dragon', '1999-04-12', '...@aol.com', 23, 5),
(13428774260001, 'Wang', 'Doctor', '1996-09-30', '...@yahoo.com', 1, 5),
(13430100000000, 'Chanz', 'Heyvery', '1994-04-04', '...@gmail.com', 107, 2)
;
您的查询解释如下所示:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
-------------------------------------------------------------------------------------------------------
| 1 | PRIMARY | students | ALL | (null) | (null) | (null) | (null) | 12 | Using where |
| 2 | SUBQUERY | students | ALL | (null) | (null) | (null) | (null) | 12 | |
我的查询中的一个是这样的:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
----------------------------------------------------------------------------------------------------
| 1 | SIMPLE | s1 | ALL | (null) | (null) | (null) | (null) | 12 | |
| 1 | SIMPLE | s2 | ALL | (null) | (null) | (null) | (null) | 12 | Using where |
差不多一样。两个查询都不使用索引,所有行都被扫描。现在我们在 column 上添加一个索引rank
。
drop table if exists students;
CREATE TABLE students
(`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int
, key rankkey(rank)
)
;
您查询的解释:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
-----------------------------------------------------------------------------------------------------------------------------
| 1 | PRIMARY | students | ref | rankkey | rankkey | 5 | const | 1 | Using where |
| 2 | SUBQUERY | (null) | (null) | (null) | (null) | (null) | (null) | (null) | Select tables optimized away |
与我的相比:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
----------------------------------------------------------------------------------------------------
| 1 | SIMPLE | s1 | ALL | (null) | (null) | (null) | (null) | 12 | |
| 1 | SIMPLE | s2 | ALL | rankkey | (null) | (null) | (null) | 12 | Using where |
您的查询使用索引,我的没有。
现在我们正在向表中添加一个主键。
drop table if exists students;
CREATE TABLE students
(`uid` bigint, `last_name` varchar(5), `first_name` varchar(8), `dob` varchar(10), `email` varchar(16), `rank` int, `grade` int
, key rankkey(rank)
, primary key(uid)
);
从您的查询中解释:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
-----------------------------------------------------------------------------------------------------------------------------
| 1 | PRIMARY | students | ref | rankkey | rankkey | 5 | const | 1 | Using where |
| 2 | SUBQUERY | (null) | (null) | (null) | (null) | (null) | (null) | (null) | Select tables optimized away |
从我的:
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
-------------------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | s1 | ALL | (null) | (null) | (null) | (null) | 12 | |
| 1 | SIMPLE | s2 | index | rankkey | rankkey | 5 | (null) | 12 | Using where; Using index; Not exists |
这样,它们很可能同样快。这就是通常构建查询和表的方式。每个表都应该有一个主键,如果你经常在排名列上运行查询过滤,你当然应该有一个索引。所以几乎没有区别。现在这一切都取决于您的表中有多少行,如果它是唯一索引和/或聚集索引。但这会导致现在有点过分了。但请注意,在此示例中,检查的行数有所不同。小数据没有区别,大数据量肯定有。但是(!)这两个查询的行为可能会改变,具体取决于索引。
如果编写查询的人犯了错误怎么办?如果他这样写怎么办:
SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.last_name IS NULL;
查询仍然有效并且有效,但是
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
----------------------------------------------------------------------------------------------------
| 1 | SIMPLE | s1 | ALL | (null) | (null) | (null) | (null) | 12 | |
| 1 | SIMPLE | s2 | ALL | rankkey | (null) | (null) | (null) | 12 | Using where |
再次不使用索引。
如果我们再次删除主键并像这样编写查询会怎样:
SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.rank IS NULL;
| ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA |
-------------------------------------------------------------------------------------------------------------------
| 1 | SIMPLE | s1 | ALL | (null) | (null) | (null) | (null) | 12 | |
| 1 | SIMPLE | s2 | index | rankkey | rankkey | 5 | (null) | 12 | Using where; Using index |
再次使用索引。
结论:如果操作正确,两个查询的运行速度应该一样快。只要索引在排名列上,您的速度就会很快。如果在编写时考虑到索引,这同样适用于我的。
希望这可以帮助。