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

Kan MySQL FIND_IN_SET eller tilsvarende fås til at bruge indekser?

Med henvisning til din kommentar:

@MarcB databasen er normaliseret, CSV-strengen kommer fra brugergrænsefladen."Få mig data for følgende personer:101.202.303"

Dette svar har et snævert fokus på netop de tal adskilt af et komma. Fordi, som det viser sig, talte du ikke engang om FIND_IN_SET trods alt.

Ja, du kan opnå, hvad du vil. Du opretter en forberedt erklæring, der accepterer en streng som en parameter som i dette Seneste svar af mine. I det svar skal du se på den anden blok, der viser CREATE PROCEDURE og dens 2. parameter, som accepterer en streng som (1,2,3) . Jeg vender tilbage til dette punkt om et øjeblik.

Ikke at du behøver at se det @spraff, men andre kan måske. Missionen er at få typen !=ALLE og mulige_nøgler og nøgler af Forklar ikke at vise null, som du viste i din anden blok. For en generel læsning om emnet, se artiklen Forståelse EXPLAIN's output og MySQL-manualsiden med titlen EXPLAIN Ekstra information .

Nu tilbage til (1,2,3) reference ovenfor. Vi ved fra din kommentar og dit andet Forklar-output i dit spørgsmål, at det opfylder følgende ønskede betingelser:

  1. type =område (og især ikke ALLE) . Se dokumenterne ovenfor om dette.
  2. nøglen er ikke null

Det er præcis de betingelser, du har i dit andet Explain-output, og det output, der kan ses med følgende forespørgsel:

Vælg * fra ratings, hvor ID i (2331425, 430364, 4557546, 269638, 4510549, 430364, 4557546, 269638, 4510549, 430364, 4557546, 269638 , ... brug din fantasi ..., ..., 4369522, 3312835);

hvor jeg har 999 værdier i den in klausulliste. Det er et eksempel fra dette svar af mine i appendiks D, så genererer en sådan tilfældig streng af csv, omgivet af åbne og lukkede parenteser.

Og bemærk følgende Forklar output for det 999-element i klausulen nedenfor:

Mål nået. Du opnår dette med en lagret proc, der ligner den, jeg nævnte før i dette link ved hjælp af en FORBEREDT UDTALELSE (og disse ting bruger concat() efterfulgt af en EXECUTE ).

Indekset er brugt, en Tablescan (betyder dårligt) opleves ikke. Yderligere læsninger er The range Join Type , enhver reference du kan finde på MySQL's Cost-Based Optimizer (CBO), dette svar fra vladr dog dateret, med øje på ANALYSE TABEL del, især efter væsentlige dataændringer. Bemærk, at ANALYSE kan tage en betydelig mængde tid at køre på ultra-store datasæt. Nogle gange mange mange timer.

Sql-injektionsangreb:

Brug af strenge, der sendes til Stored Procedures, er en angrebsvektor for SQL Injection-angreb. Forholdsregler skal være på plads for at forhindre dem ved brug af brugerleverede data. Hvis din rutine anvendes mod dine egne id'er genereret af dit system, så er du sikker. Bemærk dog, at SQL Injection-angreb på 2. niveau forekommer, når data blev sat på plads af rutiner, der ikke rensede disse data i en tidligere indsættelse eller opdatering. Angreb på plads forud for via data og brugt senere (en slags tidsindstillet bombe).

Så dette svar er færdig for det meste.

Nedenstående er en visning af den samme tabel med en mindre ændring af den for at vise, hvad en frygtet Tablescan ville se ud som i den foregående forespørgsel (men mod en ikke-indekseret kolonne kaldet thing ).

Ta et kig på vores nuværende tabeldefinition:

CREATE TABLE `ratings` ( `id` int(11) NOT NULL AUTO_INCREMENT, `thing` int(11) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=5046214 DEFAULT CHARSET=utf8;vælg min(id), max(id),count(*) som Count fra ratings;+---------+--------+------- ---+| min(id) | max(id) | theCount |+--------+---------+----------+| 1 | 5046213 | 4718592 |+---------+--------+---------+ 

Bemærk, at kolonnen thing var en nullbar int-kolonne før.

update ratings set thing=id where id<1000000;update ratings set thing=id where id>=1000000 and id<2000000;update ratings set thing=id where id>=2000000 and id<3000000;update ratings set thing=id hvor id>=3000000 og id<4000000;opdater ratings set thing=id hvor id>=4000000 og id<5100000;vælg count(*) fra vurderinger hvor thing!=id;-- 0 rowsALTER TABLE ratings MODIFY COLUMN thing int not null;-- nuværende tabeldefinition (efter ovenstående ALTER):CREATE TABLE `ratings` ( `id` int(11) NOT NULL AUTO_INCREMENT, `thing` int(11) NOT NULL, PRIMARY KEY (`id `)) ENGINE=InnoDB AUTO_INCREMENT=5046214 DEFAULT CHARSET=utf8; 

Og så Forklar, der er en Tablescan (mod kolonne thing ):




  1. Hvordan man kombinerer resultater af to forespørgsler i et enkelt datasæt

  2. Den parametriserede forespørgsel ..... forventer parameteren '@units', som ikke blev leveret

  3. PL/SQL-masseindsamling med LIMIT-klausul i Oracle-databasen

  4. Postgres UNIK BEGRÆNSNING for array