Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Selecionar valores que atendem a diferentes condições em diferentes linhas?


Ok, eu tenho downvoted sobre isso, então decidi testá-lo:
CREATE TABLE userrole (
  userid INT,
  roleid INT,
  PRIMARY KEY (userid, roleid)
);

CREATE INDEX ON userrole (roleid);

Rode isto:
<?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";função query($str) { mysql_query($str); if (mysql_error()) { echo "$str:" . mysql_error() . "\n"; }}?>
Saída:
499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.

Isso adiciona 500.000 combinações aleatórias de funções de usuário e há aproximadamente 25.000 que correspondem aos critérios escolhidos.

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

Tempo de consulta: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

Tempo de consulta:0,016s

Está certo. A versão de junção que propus é vinte vezes mais rápida que a versão agregada.

Desculpe, mas eu faço isso para viver e trabalhar no mundo real e no mundo real testamos o SQL e os resultados falam por si.

A razão para isso deve ser bastante clara. A consulta agregada será dimensionada em custo com o tamanho da tabela. Cada linha é processada, agregada e filtrada (ou não) através do HAVING cláusula. A versão de junção (usando um índice) selecionará um subconjunto de usuários com base em uma determinada função, depois verificará esse subconjunto com a segunda função e, finalmente, esse subconjunto com a terceira função. Cada seleção (em álgebra relacional termos) funciona em um subconjunto cada vez menor. A partir disso você pode concluir:

O desempenho da versão de junção fica ainda melhor com uma menor incidência de correspondências.

Se houver apenas 500 usuários (da amostra de 500 mil acima) que tenham as três funções declaradas, a versão de junção ficará significativamente mais rápida. A versão agregada não (e qualquer melhoria de desempenho é resultado do transporte de 500 usuários em vez de 25k, que a versão de junção obviamente também obtém).

Eu também estava curioso para ver como um banco de dados real (ou seja, Oracle) lidaria com isso. Então eu basicamente repeti o mesmo exercício no Oracle XE (executando na mesma máquina desktop Windows XP que o MySQL do exemplo anterior) e os resultados são quase idênticos.

As junções parecem ser desaprovadas, mas, como demonstrei, as consultas agregadas podem ser uma ordem de magnitude mais lentas.

Atualização: Após alguns testes extensivos , o quadro é mais complicado e a resposta vai depender dos seus dados, do seu banco de dados e de outros fatores. A moral da história é testar, testar, testar.