Tolong, berhenti menggunakan ORDER BY RAND()
. Berhenti saja. Operasi ini memiliki kompleksitas n*log2(n)
, yang berarti bahwa waktu yang dihabiskan untuk kueri akan bertambah "
entries | time units
-------------------------
10 | 1 /* if this takes 0.001s */
1'000 | 300
1'000'000 | 600'000 /* then this will need 10 minutes */
Jika Anda ingin menghasilkan hasil acak, buat prosedur tersimpan, yang menghasilkannya. Kira-kira seperti ini (kode diambil dari artikel ini , yang harus Anda baca):
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 ;
Dan untuk menggunakannya, Anda akan menulis:
CALL get_rands(10);
SELECT * FROM rands;
Adapun untuk menjalankan semuanya di sisi PHP, Anda harus berhenti menggunakan mysql_*
kuno API. Sudah lebih dari 10 tahun dan tidak terawat lagi. Komunitas bahkan memulai proses
untuk mencela mereka. Seharusnya tidak ada lagi kode baru yang ditulis dengan mysql_*
pada tahun 2012. Sebagai gantinya, Anda harus menggunakan PDO
atau MySQLi
. Adapun cara penulisannya (dengan 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);
}
Perbarui
Jika persyaratannya adalah untuk mendapatkan tidak hanya 10 hasil acak, tetapi sebenarnya 10 hasil acak UNIK , maka diperlukan dua perubahan pada PROCEDURE
:
-
Tabel sementara harus menerapkan keunikan entri:
CREATE TEMPORARY TABLE rands ( tagname VARCHAR(63) UNIQUE);
Mungkin juga masuk akal untuk mengumpulkan ID saja dan bukan nilainya. Apalagi jika yang Anda cari adalah 10 artikel unik, bukan sekedar tag.
-
Saat memasukkan nilai duplikat ditemukan,
cnt
counter tidak boleh berkurang. Hal ini dapat dipastikan dengan menambahkanHANDLER
(sebelum definisiLOOP
), yang akan "menangkap" peringatan yang muncul, dan menyesuaikan penghitung:DECLARE CONTINUE HANDLER FOR SQLSTATE '23000' SET cnt = cnt + 1;