sql >> Database teknologi >  >> RDS >> Mysql

Hvad er den kanoniske måde at trække en post fra en MySQL-database, der har det mindste/største felt?

Denne måde er heller ikke ualmindeligt:

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.uid IS NULL;
 

LEFT JOIN fungerer på det grundlag, at når s1.rank er på sin maksimale værdi, er der ingen s2.rank med en større værdi, og s2 rækkernes værdier vil være NULL.

Men jeg vil sige, at din måde at gøre det på er den mest almindelige, nemmeste at forstå måde at gøre det på, ja.

EDIT:På spørgsmålet, hvorfor det nogle gange er langsommere:

Ydeevnen af ​​denne forespørgsel afhænger af "hvor omhyggelig den er skrevet". Taget dine data som eksempel:

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)
;
 

Forklaringen på din forespørgsel ser sådan ud:

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

Den fra min forespørgsel som denne:

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

Næsten det samme. Ingen af ​​forespørgslerne bruger et indeks, alle rækker scannes. Nu tilføjer vi et indeks på kolonne 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)
    )
;
 

Forklaringen fra din forespørgsel:

| 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 mit:

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

Din forespørgsel bruger indekset, min gør ikke.

Nu tilføjer vi en primær nøgle til tabellen.

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)
    );
 

Forklar ud fra din forespørgsel:

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

og fra min:

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

På denne måde er de højst sandsynligt lige hurtige. Og sådan er forespørgslen og tabellen normalt opbygget. Hver tabel bør have en primær nøgle, og hvis du kører en forespørgselsfiltrering på rangkolonnen meget ofte, skal du selvfølgelig have et indeks på den. Så der er næsten ingen forskel. Det hele afhænger nu af, hvor mange rækker du har i din tabel, om det er et unikt indeks og/eller et klynget. Men det ville føre lidt for langt nu. Men bemærk, at i dette eksempel er der forskel på, hvor meget rækker der undersøges. Med små data er der ingen forskel, med store datamængder er der bestemt. Men(!) denne adfærd kan ændre sig for begge forespørgsler, afhængigt af indekset.

Hvad hvis den, der skriver forespørgslen, laver en fejl? Hvad hvis han skriver det sådan her:

SELECT s1.*
FROM students s1
LEFT JOIN students s2 ON s1.rank < s2.rank
WHERE s2.last_name IS NULL;
 

Forespørgslen virker stadig og er gyldig, men

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

igen bruges indekset ikke.

Hvad hvis vi fjerner den primære nøgle igen og skriver forespørgslen sådan her:

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 bruges igen.

Konklusion: Begge forespørgsler skal køre lige hurtigt, hvis de udføres rigtigt. Dine er hurtige, så længe et indeks er i rangkolonnen. Det samme gælder for mit, hvis det er skrevet med indeks i tankerne.

Håber dette hjælper.




  1. (har brug for råd) Tal med MySQL-serverdatabasen fra min Android-app

  2. SQL Server-databaser CPU-brugsstatistik

  3. SQL SELECT hastighed int vs varchar

  4. MySQL - hvordan man viser det seneste emne pr. tråd