Ok, jeg blev nedstemt om dette, så jeg besluttede at teste det:
OPRET TABEL brugerrolle (bruger-id INT, rolleid INT, PRIMÆR NØGLE (brugerid, rolleid));OPRET INDEKS PÅ brugerrolle (rolle);
Kør denne:
\n";mysql_connect('localhost', 'scratch', 'scratch');if (mysql_error()) { echo "Forbindelsesfejl:" . mysql_error() . "\n";}mysql_select_db('scratch');if (mysql_error()) { echo "Vælg DB-fejl:" . mysql_error() . "\n";}$brugere =200000;$count =0;for ($i=1; $i<=$brugere; $i++) { $roles =rand(1, 4); $available =range(1, 5); for ($j=0; $j<$roller; $j++) { $extract =array_splice($available, rand(0, sizeof($available)-1), 1); $id =$ekstrakt[0]; query("INSERT INTO userrole (bruger-id, rolleid) VÆRDIER ($i, $id)"); $count++; }}$stop =microtime(true);$duration =$stop - $start;$insert =$duration / $count;echo "$count brugere tilføjet.\n";echo "Programmet kørte i $duration sekunder.\n ";echo "Indsæt tid $indsæt sekunder.\n";echo "
\n";funktionsforespørgsel($str) { mysql_query($str); if (mysql_error()) { echo "$str:" . mysql_error() . "\n"; }}?> Output:
499872 brugere tilføjet.Programmet kørte i 56,5513510704 sekunder.Indsæt tid 0,000113131663847 sekunder.
Det tilføjer 500.000 tilfældige kombinationer af brugerroller, og der er cirka 25.000, der matcher de valgte kriterier.
Første forespørgsel:
SELECT useridFROM userroleWHERE roleid IN (1, 2, 3)GROUP by useridHAVING COUNT(1) =3
Forespørgselstid:0,312s
SELECT t1.useridFROM userrole t1JOIN userrole t2 ON t1.userid =t2.userid AND t2.roleid =2JOIN userrole t3 ON t2.userid =t3.userid AND t3.roleid =3AND t1
Forespørgselstid:0,016s
Det er rigtigt. Den join-version, jeg foreslog, er tyve gange hurtigere end den samlede version.
Beklager, men jeg gør dette for at leve og arbejde i den virkelige verden, og i den virkelige verden tester vi SQL, og resultaterne taler for sig selv.
Årsagen til dette burde være ret klar. Den samlede forespørgsel skaleres i pris med tabellens størrelse. Hver række behandles, aggregeres og filtreres (eller ej) gennem HAVING
klausul. Join-versionen vil (ved hjælp af et indeks) vælge en delmængde af brugerne baseret på en given rolle, derefter kontrollere den delmængde mod den anden rolle og til sidst den delmængde mod den tredje rolle. Hver udvalg
(i relationel algebra
vilkår) fungerer på en stadig mindre delmængde. Ud fra dette kan du konkludere:
Ydeevnen af join-versionen bliver endnu bedre med en lavere forekomst af matches.
Hvis der kun var 500 brugere (ud af 500.000 eksemplet ovenfor), der havde de tre angivne roller, vil join-versionen blive betydeligt hurtigere. Den samlede version vil ikke (og enhver forbedring af ydeevnen er et resultat af transport af 500 brugere i stedet for 25.000, hvilket joinversionen naturligvis også får).
Jeg var også nysgerrig efter at se, hvordan en rigtig database (dvs. Oracle) ville håndtere dette. Så jeg gentog stort set den samme øvelse på Oracle XE (kører på den samme Windows XP desktop-maskine som MySQL fra det forrige eksempel), og resultaterne er næsten identiske.
Joins ser ud til at være ilde set, men som jeg har demonstreret, kan aggregerede forespørgsler være en størrelsesorden langsommere.
Opdatering: Efter nogle omfattende test
, billedet er mere kompliceret, og svaret vil afhænge af dine data, din database og andre faktorer. Moralen i historien er test, test, test.