sql >> Database >  >> RDS >> Mysql

Waarden selecteren die aan verschillende voorwaarden voldoen op verschillende rijen?

Ok, ik kreeg een negatieve stem, dus besloot ik het te testen:

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

CREATE INDEX ON userrole (roleid);

Voer dit uit:

<?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"; }}?>

Uitgang:

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

Dat voegt 500.000 willekeurige combinaties van gebruikers en rollen toe en er zijn ongeveer 25.000 die voldoen aan de gekozen criteria.

Eerste vraag:

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

Vraagtijd: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

Vraagtijd:0.016s

Dat klopt. De join-versie die ik heb voorgesteld is twintig keer sneller dan de geaggregeerde versie.

Sorry, maar ik doe dit voor de kost en werk in de echte wereld en in de echte wereld testen we SQL en de resultaten spreken voor zich.

De reden hiervoor zou vrij duidelijk moeten zijn. De geaggregeerde query wordt in kosten geschaald met de grootte van de tabel. Elke rij wordt verwerkt, geaggregeerd en gefilterd (of niet) via de HAVING clausule. De join-versie zal (met behulp van een index) een subset van de gebruikers selecteren op basis van een bepaalde rol, en vervolgens die subset vergelijken met de tweede rol en tenslotte die subset met de derde rol. Elke selectie (in relationele algebra termen) werkt op een steeds kleinere subset. Hieruit kun je concluderen:

De prestaties van de join-versie worden nog beter met een lager aantal overeenkomsten.

Als er slechts 500 gebruikers waren (van de 500k-voorbeeld hierboven) die de drie genoemde rollen hadden, zal de join-versie aanzienlijk sneller worden. De geaggregeerde versie zal dat niet doen (en elke prestatieverbetering is het resultaat van het transporteren van 500 gebruikers in plaats van 25k, wat de join-versie uiteraard ook krijgt).

Ik was ook benieuwd hoe een echte database (dwz Oracle) hiermee om zou gaan. Dus ik herhaalde eigenlijk dezelfde oefening op Oracle XE (draaiend op dezelfde Windows XP-desktopcomputer als de MySQL uit het vorige voorbeeld) en de resultaten zijn bijna identiek.

Joins lijken afgekeurd te worden, maar zoals ik heb aangetoond, kunnen geaggregeerde zoekopdrachten een orde van grootte langzamer zijn.

Bijwerken: Na wat uitgebreide tests , het plaatje is ingewikkelder en het antwoord hangt af van uw gegevens, uw database en andere factoren. De moraal van het verhaal is test, test, test.



  1. Hoe DATE() werkt in MariaDB

  2. PostgreSQL 9-installatie op Windows:kan niet binnen het TEMP-omgevingspad schrijven.

  3. Postgres docker-container bouwen met initieel schema

  4. Hoe kan ik een back-up maken van een externe SQL Server-database naar een lokale schijf?