sql >> Databasteknik >  >> RDS >> Mysql

Välj värden som uppfyller olika villkor på olika rader?

Ok, jag blev nedröstad på detta så jag bestämde mig för att testa det:

CREATE TABLE userrole (
  userid INT,
  roleid INT,
  PRIMARY KEY (userid, roleid)
);

CREATE INDEX ON userrole (roleid);

Kör detta:

<?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_error() . "\n"; }}?>

Utdata:

499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.

Det lägger till 500 000 slumpmässiga kombinationer av användarroller och det finns cirka 25 000 som matchar de valda kriterierna.

Första frågan:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

Frågetid:0,312s

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

Frågetid:0,016s

Det är rätt. Anslutningsversionen jag föreslog är tjugo gånger snabbare än den sammanlagda versionen.

Förlåt men jag gör det här för att leva och arbeta i den verkliga världen och i den verkliga världen testar vi SQL och resultaten talar för sig själva.

Anledningen till detta borde vara ganska tydlig. Den aggregerade frågan kommer att skalas i kostnad med tabellens storlek. Varje rad bearbetas, aggregeras och filtreras (eller inte) genom HAVING klausul. Join-versionen kommer (med hjälp av ett index) att välja en delmängd av användarna baserat på en given roll, sedan kontrollera den delmängden mot den andra rollen och slutligen den delmängden mot den tredje rollen. Varje urval (i relationell algebra termer) fungerar på en allt mindre delmängd. Av detta kan du dra slutsatsen:

Prestandan för joinversionen blir ännu bättre med lägre frekvens av matchningar.

Om det bara fanns 500 användare (av provet på 500 000 ovan) som hade de tre angivna rollerna, kommer joinversionen att bli betydligt snabbare. Den sammanlagda versionen kommer inte att göra det (och någon prestandaförbättring är ett resultat av att transportera 500 användare istället för 25 000, vilket uppenbarligen också blir medlem).

Jag var också nyfiken på att se hur en riktig databas (dvs Oracle) skulle hantera detta. Så jag upprepade i princip samma övning på Oracle XE (kör på samma Windows XP-dator som MySQL från föregående exempel) och resultaten är nästan identiska.

Anslutningar verkar vara ogillade men som jag har visat kan aggregerade frågor vara en storleksordning långsammare.

Uppdatering: Efter några omfattande tester , bilden är mer komplicerad och svaret kommer att bero på din data, din databas och andra faktorer. Moralen i berättelsen är test, test, test.



  1. oracle autoincrement med sekvens och trigger fungerar inte korrekt

  2. skapa tabell med sequence.nextval i oracle

  3. MySQL &MariaDB Lastbalansering med ProxySQL

  4. Hur UCASE()-funktionen fungerar i MySQL