Mysql
 sql >> Baza danych >  >> RDS >> Mysql

Wybrać wartości, które spełniają różne warunki w różnych wierszach?

Ok, spotkałem się z krytyką, więc postanowiłem to przetestować:

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

CREATE INDEX ON userrole (roleid);

Uruchom to:

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

Wyjście:

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

To dodaje 500 000 losowych kombinacji ról użytkownika i około 25 000 spełniających wybrane kryteria.

Pierwsze zapytanie:

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

Czas zapytania: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

Czas zapytania:0,016s

Zgadza się. Zaproponowana przeze mnie wersja łączenia jest dwadzieścia razy szybsza niż wersja zbiorcza.

Przepraszam, ale robię to dla życia i pracuję w prawdziwym świecie, a w prawdziwym świecie testujemy SQL, a wyniki mówią same za siebie.

Powód tego powinien być całkiem jasny. Zapytanie agregujące będzie skalowane pod względem kosztów wraz z rozmiarem tabeli. Każdy wiersz jest przetwarzany, agregowany i filtrowany (lub nie) przez HAVING klauzula. Wersja sprzężenia (za pomocą indeksu) wybierze podzbiór użytkowników w oparciu o daną rolę, a następnie porówna ten podzbiór z drugą rolą, a na koniec ten podzbiór z trzecią rolą. Każdy wybór (w algebrze relacyjnej warunki) działa na coraz mniejszym podzbiorze. Z tego możesz wywnioskować:

Wydajność wersji dołączania staje się jeszcze lepsza przy mniejszej liczbie dopasowań.

Gdyby tylko 500 użytkowników (z powyższej próbki 500 tys.) miało trzy podane role, wersja łączenia będzie znacznie szybsza. Wersja zbiorcza nie (a jakakolwiek poprawa wydajności jest wynikiem przetransportowania 500 użytkowników zamiast 25k, które oczywiście otrzymuje również wersja dołączona).

Byłem również ciekaw, jak poradzi sobie z tym prawdziwa baza danych (tj. Oracle). Więc w zasadzie powtórzyłem to samo ćwiczenie na Oracle XE (działającym na tym samym komputerze stacjonarnym z systemem Windows XP, co MySQL z poprzedniego przykładu), a wyniki są prawie identyczne.

Połączenia wydają się być niemile widziane, ale jak pokazałem, zapytania agregujące mogą być o rząd wielkości wolniejsze.

Aktualizacja: Po kilku rozległych testach , obraz jest bardziej skomplikowany, a odpowiedź będzie zależeć od Twoich danych, bazy danych i innych czynników. Morał tej historii to test, test, test.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nie można wydać instrukcji manipulacji danymi za pomocą executeQuery()

  2. Przykład instrukcji JDBC — wstawianie, aktualizacja, usuwanie wsadowe

  3. Jak sprawdzić, czy kolumna jest pusta lub null w MySQL?

  4. Jak mogę naprawić błąd MySQL #1064?

  5. Usuwanie wierszy za pomocą MySQL LEFT JOIN