Ok, ich wurde dafür herabgestimmt, also habe ich beschlossen, es zu testen:
CREATE TABLE userrole (
userid INT,
roleid INT,
PRIMARY KEY (userid, roleid)
);
CREATE INDEX ON userrole (roleid);
Führen Sie Folgendes aus:
<?php
ini_set('max_execution_time', 120); // takes over a minute to insert 500k+ records
$start = microtime(true);
echo "<pre>\n";
mysql_connect('localhost', 'scratch', 'scratch');
if (mysql_error()) {
echo "Connect error: " . mysql_error() . "\n";
}
mysql_select_db('scratch');
if (mysql_error()) {
echo "Selct DB error: " . mysql_error() . "\n";
}
$users = 200000;
$count = 0;
for ($i=1; $i<=$users; $i++) {
$roles = rand(1, 4);
$available = range(1, 5);
for ($j=0; $j<$roles; $j++) {
$extract = array_splice($available, rand(0, sizeof($available)-1), 1);
$id = $extract[0];
query("INSERT INTO userrole (userid, roleid) VALUES ($i, $id)");
$count++;
}
}
$stop = microtime(true);
$duration = $stop - $start;
$insert = $duration / $count;
echo "$count users added.\n";
echo "Program ran for $duration seconds.\n";
echo "Insert time $insert seconds.\n";
echo "</pre>\n";
function query($str) {
mysql_query($str);
if (mysql_error()) {
echo "$str: " . mysql_error() . "\n";
}
}
?>
\n";function query($str) { mysql_query($str); if (mysql_error()) { echo "$str:" . MySQL-Fehler() . "\n"; }}?> Ausgabe:
499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.
Das fügt 500.000 zufällige Benutzer-Rollen-Kombinationen hinzu und es gibt ungefähr 25.000, die den ausgewählten Kriterien entsprechen.
Erste Abfrage:
SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3
Abfragezeit:0,312 s
SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1
Abfragezeit:0,016 s
Stimmt. Die von mir vorgeschlagene Join-Version ist zwanzig Mal schneller als die Gesamtversion
Tut mir leid, aber ich mache das für meinen Lebensunterhalt und arbeite in der realen Welt und in der realen Welt testen wir SQL und die Ergebnisse sprechen für sich.
Der Grund dafür sollte ziemlich klar sein. Die Kosten der aggregierten Abfrage werden mit der Größe der Tabelle skaliert. Jede Zeile wird durch das HAVING
verarbeitet, aggregiert und gefiltert (oder nicht). Klausel. Die Join-Version wählt (unter Verwendung eines Index) eine Teilmenge der Benutzer basierend auf einer bestimmten Rolle aus, prüft dann diese Teilmenge mit der zweiten Rolle und schließlich diese Teilmenge mit der dritten Rolle. Jede Auswahl
(in relationaler Algebra
). Begriffe) arbeitet auf einer immer kleiner werdenden Teilmenge. Daraus können Sie schließen:
Die Leistung der Join-Version wird sogar noch besser mit einer geringeren Häufigkeit von Übereinstimmungen.
Wenn nur 500 Benutzer (von den 500.000 oben genannten Beispielen) die drei angegebenen Rollen hatten, wird die Join-Version erheblich schneller. Die aggregierte Version wird dies nicht tun (und jede Leistungsverbesserung ist das Ergebnis des Transports von 500 Benutzern anstelle von 25.000, die die Join-Version offensichtlich auch erhält).
Ich war auch neugierig zu sehen, wie eine echte Datenbank (dh Oracle) damit umgehen würde. Also habe ich im Grunde die gleiche Übung auf Oracle XE wiederholt (das auf dem gleichen Windows XP-Desktop-Rechner läuft wie MySQL aus dem vorherigen Beispiel) und die Ergebnisse sind fast identisch.
Verknüpfungen scheinen verpönt zu sein, aber wie ich demonstriert habe, können aggregierte Abfragen um eine Größenordnung langsamer sein.
Aktualisierung: Nach einigen umfangreichen Tests , ist das Bild komplizierter und die Antwort hängt von Ihren Daten, Ihrer Datenbank und anderen Faktoren ab. Die Moral der Geschichte ist Test, Test, Test.