わかりました、私はこれに反対票を投じたので、それをテストすることにしました:
CREATE TABLE userrole (
userid INT,
roleid INT,
PRIMARY KEY (userid, roleid)
);
CREATE INDEX ON userrole (roleid);
これを実行します:
<?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";
}
}
?>
出力:
499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.
これにより、500,000のランダムなユーザーと役割の組み合わせが追加され、選択した基準に一致する約25,000があります。
最初のクエリ:
SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3
クエリ時間:0.312秒
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
クエリ時間:0.016秒
それは正しい。私が提案した結合バージョンは、集約バージョンよりも20倍高速です。
申し訳ありませんが、私はこれを現実の世界での生活と仕事のために行っています。現実の世界ではSQLをテストし、結果がそれを物語っています。
この理由はかなり明確なはずです。集計クエリは、テーブルのサイズに応じてコストがスケーリングされます。すべての行は、HAVING
句を介して処理、集約、およびフィルタリングされます(またはフィルタリングされません)。参加バージョンは、(インデックスを使用して)特定の役割に基づいてユーザーのサブセットを選択し、そのサブセットを2番目の役割と照合し、最後にそのサブセットを3番目の役割と照合します。各選択(関係代数の用語で)は、ますます小さなサブセットで機能します。これから、次のように結論付けることができます。
結合バージョンのパフォーマンスは、一致の発生率が低くなるとさらに向上します。
上記の3つの役割を持つユーザーが500人(上記の500kサンプルのうち)しかない場合、参加バージョンは大幅に高速化されます。集約バージョンはそうではありません(そして、パフォーマンスの向上は、25kではなく500ユーザーを転送した結果であり、結合バージョンも明らかに得られます)。
また、実際のデータベース(つまり、Oracle)がこれをどのように処理するかについても興味がありました。したがって、基本的にOracle XE(前の例のMySQLと同じWindows XPデスクトップマシンで実行)で同じ演習を繰り返しましたが、結果はほぼ同じです。
結合は眉をひそめているように見えますが、私が示したように、集約クエリは桁違いに遅くなる可能性があります。
更新:いくつかの広範なテストの後、全体像はより複雑になり、答えはデータ、データベース、およびその他の要因によって異なります。物語の教訓は、テスト、テスト、テストです。