Vui lòng ngừng sử dụng ORDER BY RAND()
. Chỉ dừng lại. Thao tác này có độ phức tạp là n*log2(n)
, có nghĩa là thời gian dành cho truy vấn sẽ tăng lên "
entries | time units
-------------------------
10 | 1 /* if this takes 0.001s */
1'000 | 300
1'000'000 | 600'000 /* then this will need 10 minutes */
Nếu bạn muốn tạo các kết quả ngẫu nhiên, hãy tạo một thủ tục được lưu trữ để tạo ra chúng. Một cái gì đó như thế này (mã được lấy từ bài viết này , mà bạn nên đọc):
DELIMITER $$
DROP PROCEDURE IF EXISTS get_rands$$
CREATE PROCEDURE get_rands(IN cnt INT)
BEGIN
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET cnt = cnt - 1;
INSERT INTO rands
SELECT tags.tagname
FROM tags
JOIN (SELECT (RAND()*(SELECT MAX(tags.id) FROM tags)) AS id) AS choices
WHERE tags.id >= choices.id
LIMIT 1;
END LOOP loop_me;
END$$
DELIMITER ;
Và để sử dụng nó, bạn sẽ viết:
CALL get_rands(10);
SELECT * FROM rands;
Đối với việc thực thi tất cả trên PHP, bạn nên ngừng sử dụng mysql_*
cổ API. Nó đã hơn 10 năm tuổi và không còn được duy trì. Cộng đồng thậm chí đã bắt đầu quá trình
vì không dùng chúng nữa. Không nên có thêm bất kỳ mã mới nào được viết bằng mysql_*
vào năm 2012. Thay vào đó, bạn nên sử dụng PDO
hoặc MySQLi
. Về cách viết nó (với PDO):
// creates DB connection
$connection = new PDO('mysql:host=localhost;dbname=mydb;charset=UTF-8',
'username', 'password');
$connection->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// executes the procedure and creates select statement
$connection->exec('CALL get_rands(10)');
$statement = $connection->query('SELECT * FROM rands');
// performs query and collects all the info
if ($statement->execute())
{
$tags = $statement->fetchAll(PDO::FETCH::ASSOC);
}
Cập nhật
Nếu yêu cầu là nhận được không chỉ 10 kết quả ngẫu nhiên, mà thực sự là 10 kết quả ngẫu nhiên DUY NHẤT , thì nó sẽ yêu cầu hai thay đổi đối với PROCEDURE
:
-
Bảng tạm thời phải thực thi tính duy nhất của các mục nhập:
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
Nó cũng có thể có ý nghĩa khi chỉ thu thập các ID chứ không phải các giá trị. Đặc biệt nếu những gì bạn đang tìm kiếm là 10 bài báo độc đáo, không chỉ các thẻ.
-
Khi phát hiện thấy việc chèn một giá trị trùng lặp,
cnt
bộ đếm không được giảm. Điều này có thể được đảm bảo bằng cách thêmHANDLER
(trước định nghĩa củaLOOP
), sẽ "bắt" cảnh báo đã nêu và điều chỉnh bộ đếm:DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;