Ok, dostal jsem za to záporný hlas, takže jsem se rozhodl to otestovat:
CREATE TABLE userrole (
userid INT,
roleid INT,
PRIMARY KEY (userid, roleid)
);
CREATE INDEX ON userrole (roleid);
Spusťte toto:
<?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";funkce dotaz($str) { mysql_query($str); if (mysql_error()) { echo "$str:" . mysql_error() . "\n"; }}?> Výstup:
499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.
To přidá 500 000 náhodných kombinací uživatelských rolí a přibližně 25 000 odpovídá zvoleným kritériím.
První dotaz:
SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3
Doba dotazu: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
Doba dotazu:0,016 s
To je správně. Verze pro připojení, kterou jsem navrhl, je dvacetkrát rychlejší než souhrnná verze.
Je mi líto, ale živím se tím a pracuji v reálném světě a v reálném světě testujeme SQL a výsledky mluví samy za sebe.
Důvod by měl být celkem jasný. Souhrnný dotaz bude škálovat náklady s velikostí tabulky. Každý řádek je zpracován, agregován a filtrován (nebo nikoli) prostřednictvím HAVING
doložka. Verze spojení vybere (pomocí indexu) podmnožinu uživatelů na základě dané role, poté tuto podmnožinu porovná s druhou rolí a nakonec tuto podmnožinu porovná s třetí rolí. Každý výběr
(v relační algebře
termíny) funguje na stále menší podmnožině. Z toho můžete vyvodit závěr:
Výkon připojené verze je ještě lepší s nižším výskytem shod.
Pokud by bylo pouze 500 uživatelů (z výše uvedeného vzorku 500 000), kteří měli tři uvedené role, verze pro připojení bude výrazně rychlejší. Souhrnná verze nebude (a jakékoli zlepšení výkonu je výsledkem přepravy 500 uživatelů namísto 25 000, což samozřejmě dostane i verze pro připojení).
Také jsem byl zvědavý, jak se s tím vypořádá skutečná databáze (tj. Oracle). Takže jsem v podstatě zopakoval stejné cvičení na Oracle XE (běžícím na stejném stolním počítači s Windows XP jako MySQL z předchozího příkladu) a výsledky jsou téměř totožné.
Zdá se, že spojení jsou odsuzována, ale jak jsem ukázal, souhrnné dotazy mohou být o řád pomalejší.
Aktualizace: Po nějakém rozsáhlém testování , obrázek je složitější a odpověď bude záviset na vašich datech, vaší databázi a dalších faktorech. Morálka příběhu je test, test, test.