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

SELECT med forespørgselsvariabler, der ikke bruger INDEX'er

Årsagen ligger i brugen af ​​ELLER forhold i Hvor klausul.

For at illustrere det, prøv at køre forespørgslen igen, denne gang med kun id = 5 betingelse, og få (EXPLAIN output):

+----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 1 | PRIMARY | tree | const | PRIMARY,index_both | PRIMARY | 4 | const | 1 | | | 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used | +----+-------------+------------+--------+--------------------+---------+---------+-------+------+----------------+

Og igen, denne gang kun med parent_id = @last_id OR parent_id = 5 tilstand, og få:

+----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | +----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+ | 1 | PRIMARY | <derived2> | system | NULL | NULL | NULL | NULL | 1 | | | 1 | PRIMARY | tree | ALL | index_parent_id | NULL | NULL | NULL | 10 | Using where | | 2 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used | +----+-------------+------------+--------+-----------------+------+---------+------+------+----------------+

MySQL er ikke så god til at håndtere flere indekser i samme forespørgsel. Det går lidt bedre med OG-betingelser; man er mere tilbøjelig til at se en index_merge optimering end en indeksunion optimering.

Tingene bliver bedre, efterhånden som versionerne skrider frem, men jeg har testet din forespørgsel på version 5.5 , som er den seneste produktionsversion, og resultaterne er som du beskriver.

For at forklare, hvorfor dette er svært, skal du overveje:to forskellige indekser vil svare for to forskellige betingelser for forespørgslen. Man vil svare for id = 5 , den anden for parent_id = @last_id OR parent_id = 5 (BTW intet problem med ELLER inde i sidstnævnte, da begge udtryk håndteres fra samme indeks).

Der er ikke et enkelt indeks, der kan svare for begge, og derfor FORCE INDEX instruktion ignoreres. Se FORCE INDEX siger, at MySQL skal bruge en indeks over en tabelscanning. Det betyder ikke, at det skal bruge mere end ét indeks over en tabelscanning.

Så MySQL følger reglerne for dokumentationen her. Men hvorfor er dette så kompliceret? Fordi at svare ved at bruge begge indekser, er MySQL nødt til at indsamle resultater fra begge, gemme ens til side i en midlertidig buffer, mens man administrerer den anden. Så er det nødt til at gå over den buffer for at filtrere identiske rækker fra (det er muligt, at en række passer til alle betingelser). Og så for at scanne bufferen for at returnere resultaterne.

Men vent, den buffer er i sig selv ikke indekseret. Filtrering af dubletter er ikke en oplagt opgave. Så MySQL foretrækker at arbejde på den originale tabel og lave scanningen der og undgå alt det rod.

Dette er selvfølgelig løseligt. Ingeniørerne hos Oracle kan forbedre dette endnu (for nylig har de arbejdet hårdt på at forbedre planerne for udførelse af forespørgsler), men jeg ved ikke, om dette er på TODO-opgaven, eller om det har en høj prioritet.




  1. Brug af 'LIKE' med resultatet af en SQL-underforespørgsel

  2. Sådan kontrollerer du MySQL-database- og tabelstørrelser

  3. hvordan man beregner saldi i et regnskabssoftware ved hjælp af postgres vinduesfunktion

  4. Indsættelse af billede i BLOB Oracle 10g