Ten sposób również nie jest niczym niezwykłym:
SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.uid IS NULL;
LEFT JOIN działa na podstawie tego, że gdy s1.rank osiąga maksymalną wartość, nie ma s2.rank o większej wartości, a wartości wierszy s2 będą równe NULL.
Ale powiedziałbym, że twój sposób robienia tego jest najczęstszym, najłatwiejszym do zrozumienia sposobem robienia tego, tak.
EDYCJA:Na pytanie, dlaczego czasami jest wolniej:
Wydajność tego zapytania zależy od tego, „jak starannie zostało napisane”. Weź swoje dane jako przykład:
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', '[email protected]', 99, 4),
(13428721960000, 'Li', 'Kai Li', '1979-02-15', '[email protected]', 12, 2),
(13428722180001, 'Zhang', 'Xi Xiong', '1993-11-09', '[email protected]', 5, 5),
(13428739950000, 'Zhou', 'Ji Hai', '1991-06-06', '[email protected]', 234, 1),
(13428739950001, 'Pan', 'Yao', '1992-05-12', '[email protected]', 43, 2),
(13428740010001, 'Jin', 'Denny', '1994-06-02', '[email protected]', 198, 3),
(13428740010002, 'Li', 'Fonzie', '1991-02-02', '[email protected]', 75, 3),
(13428743370000, 'Ma', 'Haggar', '1991-08-16', '[email protected]', 47, 4),
(13428743590001, 'Ren', 'Jenny', '1990-03-29', '[email protected]', 5, 2),
(13428774040000, 'Chen', 'Dragon', '1999-04-12', '[email protected]', 23, 5),
(13428774260001, 'Wang', 'Doctor', '1996-09-30', '[email protected]', 1, 5),
(13430100000000, 'Chanz', 'Heyvery', '1994-04-04', '[email protected]', 107, 2)
;
Wyjaśnienie zapytania wygląda tak:
| 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 | |
Ten z mojego zapytania wygląda tak:
| 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 |
Prawie to samo. Żadne z zapytań nie używa indeksu, skanowane są wszystkie wiersze. Teraz dodajemy indeks w kolumnie 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)
)
;
Wyjaśnienie Twojego zapytania:
| 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 |
kontra mój:
| 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 |
Twoje zapytanie korzysta z indeksu, moje nie.
Teraz dodajemy klucz podstawowy do tabeli.
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)
);
Wyjaśnij w swoim zapytaniu:
| 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 |
i z mojego:
| 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 |
W ten sposób najprawdopodobniej są równie szybkie. I tak zwykle buduje się zapytanie i tabelę. Każda tabela powinna mieć klucz podstawowy i jeśli bardzo często używasz filtrowania zapytań na kolumnie rangi, powinieneś oczywiście mieć na niej indeks. Więc nie ma prawie żadnej różnicy. Wszystko zależy teraz od tego, ile masz wierszy w swojej tabeli, czy jest to unikalny indeks i/lub klastrowany. Ale to prowadziłoby teraz trochę za daleko. Należy jednak pamiętać, że w tym przykładzie jest różnica w ilości sprawdzanych wierszy. Przy małych ilościach danych nie ma różnicy, przy dużych ilościach danych na pewno. Ale(!) to zachowanie może się zmienić dla obu zapytań, w zależności od indeksu.
Co zrobić, jeśli ten, kto pisze zapytanie, popełni błąd? A jeśli napisze to w ten sposób:
SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.last_name IS NULL;
Zapytanie nadal działa i jest prawidłowe, ale
| 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 |
ponownie indeks nie jest używany.
Co się stanie, jeśli ponownie usuniemy klucz główny i napiszemy zapytanie w ten sposób:
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 |
Indeks jest ponownie używany.
Wniosek: Oba zapytania powinny działać równie szybko, jeśli zostaną wykonane prawidłowo. Twoje jest szybkie, o ile indeks znajduje się w kolumnie rang. To samo dotyczy mojego, jeśli jest pisane z myślą o indeksach.
Mam nadzieję, że to pomoże.