Được rồi, tôi đã phản đối về điều này nên tôi quyết định thử nghiệm nó:
CREATE TABLE userrole (
userid INT,
roleid INT,
PRIMARY KEY (userid, roleid)
);
CREATE INDEX ON userrole (roleid);
Chạy cái này:
<?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"; }}?> Đầu ra:
499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.
Điều đó thêm 500.000 kết hợp vai trò người dùng ngẫu nhiên và có khoảng 25.000 kết hợp phù hợp với tiêu chí đã chọn.
Truy vấn đầu tiên:
SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3
Thời gian truy vấn:0,312 giây
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
Thời gian truy vấn:0,016 giây
Đúng rồi. Phiên bản tham gia mà tôi đề xuất nhanh hơn hai mươi lần so với phiên bản tổng hợp.
Xin lỗi nhưng tôi làm điều này để kiếm sống và làm việc trong thế giới thực và trong thế giới thực, chúng tôi kiểm tra SQL và kết quả tự nói lên.
Lý do cho điều này nên khá rõ ràng. Truy vấn tổng hợp sẽ chia tỷ lệ chi phí với kích thước của bảng. Mọi hàng được xử lý, tổng hợp và lọc (hoặc không) thông qua HAVING
mệnh đề. Phiên bản tham gia sẽ (sử dụng chỉ mục) chọn một tập hợp con người dùng dựa trên một vai trò nhất định, sau đó kiểm tra tập hợp con đó so với vai trò thứ hai và cuối cùng là tập hợp con đó so với vai trò thứ ba. Mỗi lựa chọn
(trong đại số quan hệ
điều khoản) hoạt động trên một tập hợp con ngày càng nhỏ. Từ đó bạn có thể kết luận:
Hiệu suất của phiên bản tham gia thậm chí còn tốt hơn với tỷ lệ trùng khớp thấp hơn.
Nếu chỉ có 500 người dùng (trong số 500k mẫu ở trên) có ba vai trò đã nêu, phiên bản tham gia sẽ nhanh hơn đáng kể. Phiên bản tổng hợp sẽ không (và bất kỳ sự cải thiện nào về hiệu suất là kết quả của việc vận chuyển 500 người dùng thay vì 25 nghìn, mà phiên bản tham gia hiển nhiên cũng nhận được).
Tôi cũng tò mò muốn xem một cơ sở dữ liệu thực (tức là Oracle) sẽ giải quyết vấn đề này như thế nào. Vì vậy, về cơ bản tôi đã lặp lại bài tập tương tự trên Oracle XE (chạy trên cùng một máy tính để bàn Windows XP với MySQL từ ví dụ trước) và kết quả gần như giống hệt nhau.
Các phép tham gia dường như không được chấp nhận nhưng như tôi đã chứng minh, các truy vấn tổng hợp có thể chậm hơn theo thứ tự lớn hơn.
Cập nhật: Sau một số thử nghiệm rộng rãi , bức tranh phức tạp hơn và câu trả lời sẽ phụ thuộc vào dữ liệu của bạn, cơ sở dữ liệu của bạn và các yếu tố khác. Đạo đức của câu chuyện là kiểm tra, kiểm tra, kiểm tra.