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

Selezionare valori che soddisfano condizioni diverse su righe diverse?

Ok, ho ottenuto un voto negativo su questo, quindi ho deciso di testarlo:

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

CREATE INDEX ON userrole (roleid);

Esegui questo:

<?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";funzione query($str) { mysql_query($str); if (mysql_error()) { echo "$str:" . mysql_error() . "\n"; }}?>

Uscita:

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

Ciò aggiunge 500.000 combinazioni casuali di ruoli utente e ce ne sono circa 25.000 che corrispondono ai criteri scelti.

Prima domanda:

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

Tempo di interrogazione: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

Tempo di interrogazione:0,016 s

Giusto. La versione di join che ho proposto è venti volte più veloce della versione aggregata.

Scusa ma lo faccio per vivere e lavoro nel mondo reale e nel mondo reale testiamo SQL e i risultati parlano da soli.

Il motivo dovrebbe essere abbastanza chiaro. La query aggregata aumenterà il costo in base alle dimensioni della tabella. Ogni riga viene elaborata, aggregata e filtrata (o meno) tramite il HAVING clausola. La versione di join selezionerà (utilizzando un indice) un sottoinsieme di utenti in base a un determinato ruolo, quindi verificherà quel sottoinsieme rispetto al secondo ruolo e infine quel sottoinsieme rispetto al terzo ruolo. Ogni selezione (in algebra relazionale termini) funziona su un sottoinsieme sempre più piccolo. Da questo puoi concludere:

Le prestazioni della versione join migliorano ulteriormente con una minore incidenza di corrispondenze.

Se c'erano solo 500 utenti (su un campione di 500.000 sopra) con i tre ruoli indicati, la versione di join sarebbe notevolmente più veloce. La versione aggregata non lo farà (e qualsiasi miglioramento delle prestazioni è il risultato del trasporto di 500 utenti invece di 25k, cosa che ovviamente ottiene anche la versione di join).

Ero anche curioso di vedere come un vero database (ovvero Oracle) si sarebbe comportato con questo. Quindi ho praticamente ripetuto lo stesso esercizio su Oracle XE (in esecuzione sulla stessa macchina desktop Windows XP di MySQL dell'esempio precedente) e i risultati sono quasi identici.

I join sembrano essere disapprovati ma, come ho dimostrato, le query aggregate possono essere un ordine di grandezza più lente.

Aggiornamento: Dopo alcuni test approfonditi , il quadro è più complicato e la risposta dipenderà dai tuoi dati, dal tuo database e da altri fattori. La morale della favola è prova, prova, prova.