Câu trả lời:
Một bài đăng tuyệt vời xử lý một số trường hợp, từ đơn giản, đến khoảng trống, không đồng nhất với các khoảng trống.
http://jan.kneschke.de/projects/mysql/order-by-rand/
Đối với hầu hết các trường hợp chung, đây là cách bạn làm điều đó:
SELECT name
FROM random AS r1 JOIN
(SELECT CEIL(RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1
Điều này cho rằng sự phân phối của id là bằng nhau và có thể có những khoảng trống trong danh sách id. Xem bài viết cho các ví dụ nâng cao hơn
mysqli_fetch_assoc($result)
? Hay là 10 kết quả không nhất thiết phải phân biệt?
SELECT column FROM table
ORDER BY RAND()
LIMIT 10
Không phải là giải pháp hiệu quả nhưng hoạt động
ORDER BY RAND()
tương đối chậm
SELECT words, transcription, translation, sound FROM vocabulary WHERE menu_id=$menuId ORDER BY RAND() LIMIT 10
mất 0,0010, không có GIỚI HẠN 10, nó mất 0,0012 (trong bảng đó 3500 từ).
Truy vấn đơn giản có hiệu suất tuyệt vời và hoạt động với các khoảng trống :
SELECT * FROM tbl AS t1 JOIN (SELECT id FROM tbl ORDER BY RAND() LIMIT 10) as t2 ON t1.id=t2.id
Truy vấn này trên bảng 200K mất 0,08 giây và phiên bản bình thường (CHỌN * TỪ Tbl ĐẶT HÀNG B RNG RAND () GIỚI HẠN 10) mất B RNG GIỚI 0,35 giây trên máy của tôi.
Điều này nhanh vì giai đoạn sắp xếp chỉ sử dụng cột ID được lập chỉ mục. Bạn có thể thấy hành vi này trong phần giải thích:
CHỌN * TỪ Tbl ĐẶT HÀNG B RNG RAND () GIỚI HẠN 10:
CHỌN * TỪ tbl NHƯ T1 THAM GIA (CHỌN id TỪ Tbl ĐẶT HÀNG B RNG RAND () GIỚI HẠN 10) là t2 TRÊN t1.id = t2.id
Phiên bản có trọng số : https://stackoverflow.com/a/41577458/893432
Tôi đang nhận được các truy vấn nhanh (khoảng 0,5 giây) với một cpu chậm , chọn 10 hàng ngẫu nhiên trong 400K đăng ký cơ sở dữ liệu MySQL kích thước 2Gb không được lưu trong bộ nhớ cache. Xem ở đây mã của tôi: Lựa chọn nhanh các hàng ngẫu nhiên trong MySQL
<?php
$time= microtime_float();
$sql='SELECT COUNT(*) FROM pages';
$rquery= BD_Ejecutar($sql);
list($num_records)=mysql_fetch_row($rquery);
mysql_free_result($rquery);
$sql="SELECT id FROM pages WHERE RAND()*$num_records<20
ORDER BY RAND() LIMIT 0,10";
$rquery= BD_Ejecutar($sql);
while(list($id)=mysql_fetch_row($rquery)){
if($id_in) $id_in.=",$id";
else $id_in="$id";
}
mysql_free_result($rquery);
$sql="SELECT id,url FROM pages WHERE id IN($id_in)";
$rquery= BD_Ejecutar($sql);
while(list($id,$url)=mysql_fetch_row($rquery)){
logger("$id, $url",1);
}
mysql_free_result($rquery);
$time= microtime_float()-$time;
logger("num_records=$num_records",1);
logger("$id_in",1);
logger("Time elapsed: <b>$time segundos</b>",1);
?>
ORDER BY RAND()
FLUSH STATUS; SELECT ...; SHOW SESSION STATUS LIKE 'Handler%';
để xem nó.
ORDER BY RAND()
là nó chỉ sắp xếp các id (không phải hàng đầy đủ), vì vậy bảng tạm thời nhỏ hơn, nhưng vẫn phải sắp xếp tất cả chúng.
Nó rất đơn giản và truy vấn dòng đơn.
SELECT * FROM Table_Name ORDER BY RAND() LIMIT 0,10;
order by rand()
rất chậm nếu bàn lớn
Từ cuốn sách:
Chọn một hàng ngẫu nhiên bằng cách sử dụng một offset
Một kỹ thuật khác để tránh các vấn đề được tìm thấy trong các lựa chọn thay thế trước đó là đếm các hàng trong tập dữ liệu và trả về một số ngẫu nhiên trong khoảng từ 0 đến số đếm. Sau đó sử dụng số này làm phần bù khi truy vấn tập dữ liệu
<?php
$rand = "SELECT ROUND(RAND() * (SELECT COUNT(*) FROM Bugs))";
$offset = $pdo->query($rand)->fetch(PDO::FETCH_ASSOC);
$sql = "SELECT * FROM Bugs LIMIT 1 OFFSET :offset";
$stmt = $pdo->prepare($sql);
$stmt->execute( $offset );
$rand_bug = $stmt->fetch();
Sử dụng giải pháp này khi bạn không thể giả sử các giá trị khóa liền kề và bạn cần đảm bảo mỗi hàng có cơ hội được chọn.
SELECT count(*)
trở nên chậm.
Cách chọn các hàng ngẫu nhiên từ một bảng:
Từ đây: Chọn các hàng ngẫu nhiên trong MySQL
Một cải tiến nhanh chóng so với "quét bảng" là sử dụng chỉ mục để nhận id ngẫu nhiên.
SELECT *
FROM random, (
SELECT id AS sid
FROM random
ORDER BY RAND( )
LIMIT 10
) tmp
WHERE random.id = tmp.sid;
PRIMARY KEY
).
Chà, nếu bạn không có khoảng trống trong các phím của mình và chúng đều là số, bạn có thể tính các số ngẫu nhiên và chọn các dòng đó. nhưng điều này có thể sẽ không xảy ra
Vì vậy, một giải pháp sẽ là như sau:
SELECT * FROM table WHERE key >= FLOOR(RAND()*MAX(id)) LIMIT 1
về cơ bản sẽ đảm bảo rằng bạn nhận được một số ngẫu nhiên trong phạm vi các khóa của mình và sau đó bạn chọn số tốt nhất tiếp theo lớn hơn. bạn phải làm điều này 10 lần
tuy nhiên điều này KHÔNG thực sự ngẫu nhiên vì các khóa của bạn rất có thể sẽ không được phân phối đều.
Đây thực sự là một vấn đề lớn và không dễ để giải quyết đáp ứng tất cả các yêu cầu, rand () của MySQL là điều tốt nhất bạn có thể nhận được nếu bạn thực sự muốn 10 hàng ngẫu nhiên.
Tuy nhiên, có một giải pháp khác nhanh nhưng cũng có sự đánh đổi khi nói đến sự ngẫu nhiên, nhưng có thể phù hợp với bạn hơn. Đọc về nó ở đây: Làm thế nào tôi có thể tối ưu hóa chức năng ORDER BY RAND () của MySQL?
Câu hỏi là ngẫu nhiên như thế nào bạn cần nó được.
Bạn có thể giải thích thêm một chút để tôi có thể cung cấp cho bạn một giải pháp tốt.
Ví dụ, một công ty tôi làm việc cùng có một giải pháp mà họ cần sự ngẫu nhiên tuyệt đối cực kỳ nhanh. Họ đã kết thúc với việc điền trước cơ sở dữ liệu với các giá trị ngẫu nhiên được chọn giảm dần và được đặt thành các giá trị ngẫu nhiên khác nhau sau đó một lần nữa.
Nếu bạn hầu như không bao giờ cập nhật, bạn cũng có thể điền vào một id tăng dần để bạn không có khoảng trống và chỉ có thể tính toán các khóa ngẫu nhiên trước khi chọn ... Nó phụ thuộc vào trường hợp sử dụng!
Id
và tất cả các truy vấn ngẫu nhiên của bạn sẽ trả về cho bạn cái đó Id
.
FLOOR(RAND()*MAX(id))
thiên về việc trả lại id lớn hơn.
Tôi cần một truy vấn để trả về một số lượng lớn các hàng ngẫu nhiên từ một bảng khá lớn. Đây là những gì tôi đã đưa ra. Đầu tiên nhận id hồ sơ tối đa:
SELECT MAX(id) FROM table_name;
Sau đó thay thế giá trị đó thành:
SELECT * FROM table_name WHERE id > FLOOR(RAND() * max) LIMIT n;
Trong đó max là id bản ghi tối đa trong bảng và n là số lượng hàng bạn muốn trong tập kết quả của bạn. Giả định là không có lỗ hổng nào trong id hồ sơ mặc dù tôi nghi ngờ nó sẽ ảnh hưởng đến kết quả nếu có (mặc dù chưa thử). Tôi cũng đã tạo thủ tục lưu trữ này để chung chung hơn; vượt qua trong tên bảng và số lượng hàng được trả lại. Tôi đang chạy MySQL 5.5,38 trên Windows 2008, 32GB, 3G54 E5450 kép và trên một bảng có 17.361.264 hàng, nó khá nhất quán ở ~ .03 giây / ~ 11 giây để trả về 1.000.000 hàng. (thời gian là từ MySQL Workbench 6.1; bạn cũng có thể sử dụng CEIL thay vì FLOOR trong câu lệnh chọn thứ 2 tùy theo sở thích của bạn)
DELIMITER $$
USE [schema name] $$
DROP PROCEDURE IF EXISTS `random_rows` $$
CREATE PROCEDURE `random_rows`(IN tab_name VARCHAR(64), IN num_rows INT)
BEGIN
SET @t = CONCAT('SET @max=(SELECT MAX(id) FROM ',tab_name,')');
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
SET @t = CONCAT(
'SELECT * FROM ',
tab_name,
' WHERE id>FLOOR(RAND()*@max) LIMIT ',
num_rows);
PREPARE stmt FROM @t;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
END
$$
sau đó
CALL [schema name].random_rows([table name], n);
Tất cả các câu trả lời hay nhất đã được đăng (chủ yếu là những câu trả lời liên kết http://jan.kneschke.de/projects/mysql/order-by-rand/ ).
Tôi muốn xác định một khả năng tăng tốc khác - bộ nhớ đệm . Hãy nghĩ về lý do tại sao bạn cần nhận được các hàng ngẫu nhiên. Có lẽ bạn muốn hiển thị một số bài đăng ngẫu nhiên hoặc quảng cáo ngẫu nhiên trên một trang web. Nếu bạn đang nhận được 100 req / s, có thực sự cần thiết rằng mỗi khách truy cập có được các hàng ngẫu nhiên không? Thông thường sẽ hoàn toàn ổn khi lưu trữ các hàng X ngẫu nhiên này trong 1 giây (hoặc thậm chí 10 giây). Sẽ không có vấn đề gì nếu 100 khách truy cập duy nhất trong cùng 1 giây nhận được cùng một bài đăng ngẫu nhiên, bởi vì trong 100 giây tiếp theo, 100 khách truy cập khác sẽ nhận được các nhóm bài đăng khác nhau.
Khi sử dụng bộ đệm này, bạn cũng có thể sử dụng một số giải pháp chậm hơn để nhận dữ liệu ngẫu nhiên vì nó sẽ được tìm nạp từ MySQL chỉ một lần mỗi giây bất kể req / s của bạn.
Tôi đã cải thiện câu trả lời @Riedsio. Đây là truy vấn hiệu quả nhất mà tôi có thể tìm thấy trên một bảng lớn, được phân phối đồng đều với các khoảng trống (được thử nghiệm khi nhận 1000 hàng ngẫu nhiên từ một bảng có> 2,6B hàng).
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
Hãy để tôi giải nén những gì đang xảy ra.
@max := (SELECT MAX(id) FROM table)
MAX(id)
mỗi khi bạn cần một hàngSELECT FLOOR(rand() * @max) + 1 as rand)
SELECT id FROM table INNER JOIN (...) on id > rand LIMIT 1
Thực hiện liên kết giúp bạn điều chỉnh mọi thứ thành 1 truy vấn để bạn có thể tránh thực hiện nhiều truy vấn. Nó cũng cho phép bạn tiết kiệm chi phí tính toán MAX(id)
. Tùy thuộc vào ứng dụng của bạn, điều này có thể quan trọng rất nhiều hoặc rất ít.
Lưu ý rằng điều này chỉ nhận được id và nhận chúng theo thứ tự ngẫu nhiên. Nếu bạn muốn làm bất cứ điều gì cao cấp hơn, tôi khuyên bạn nên làm điều này:
SELECT t.id, t.name -- etc, etc
FROM table t
INNER JOIN (
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max := (SELECT MAX(id) FROM table)) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1) UNION
(SELECT id FROM table INNER JOIN (SELECT FLOOR(RAND() * @max) + 1 as rand) r on id > rand LIMIT 1)
) x ON x.id = t.id
ORDER BY t.id
LIMIT 1
để LIMIT 30
ở khắp mọi nơi trong truy vấn
LIMIT 1
để LIMIT 30
có thể giúp bạn có được 30 bản ghi trong một hàng từ một điểm ngẫu nhiên trong bảng. Thay vào đó, bạn nên có 30 bản sao của (SELECT id FROM ....
phần ở giữa.
Riedsio
trả lời. Tôi đã thử với 500 lần truy cập mỗi giây vào trang bằng PHP 7.0.22 và MariaDB trên centos 7, với Riedsio
câu trả lời tôi đã nhận được hơn 500 phản hồi thành công sau đó là câu trả lời của bạn.
Tôi đã sử dụng http://jan.kneschke.de/projects/mysql/order-by-rand/ được đăng bởi Riedsio (tôi đã sử dụng trường hợp của một thủ tục được lưu trữ trả về một hoặc nhiều giá trị ngẫu nhiên):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
INSERT INTO rands
SELECT r1.id
FROM random AS r1 JOIN
(SELECT (RAND() *
(SELECT MAX(id)
FROM random)) AS id)
AS r2
WHERE r1.id >= r2.id
ORDER BY r1.id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
Trong bài viết, ông giải quyết vấn đề về khoảng trống trong id gây ra kết quả không ngẫu nhiên bằng cách duy trì bảng (sử dụng trình kích hoạt, v.v ... xem bài viết); Tôi đang giải quyết vấn đề bằng cách thêm một cột khác vào bảng, được điền với các số liền kề, bắt đầu từ 1 ( chỉnh sửa: cột này được thêm vào bảng tạm thời được tạo bởi truy vấn con khi chạy, không ảnh hưởng đến bảng cố định của bạn):
DROP TEMPORARY TABLE IF EXISTS rands;
CREATE TEMPORARY TABLE rands ( rand_id INT );
loop_me: LOOP
IF cnt < 1 THEN
LEAVE loop_me;
END IF;
SET @no_gaps_id := 0;
INSERT INTO rands
SELECT r1.id
FROM (SELECT id, @no_gaps_id := @no_gaps_id + 1 AS no_gaps_id FROM random) AS r1 JOIN
(SELECT (RAND() *
(SELECT COUNT(*)
FROM random)) AS id)
AS r2
WHERE r1.no_gaps_id >= r2.id
ORDER BY r1.no_gaps_id ASC
LIMIT 1;
SET cnt = cnt - 1;
END LOOP loop_me;
Trong bài viết tôi có thể thấy anh ấy đã đi rất lâu để tối ưu hóa mã; Tôi không có ý tưởng nếu / bao nhiêu thay đổi của tôi ảnh hưởng đến hiệu suất nhưng hoạt động rất tốt cho tôi.
@no_gaps_id
không có chỉ mục nào có thể được sử dụng, vì vậy nếu bạn xem xét EXPLAIN
truy vấn của mình, bạn có Using filesort
và Using where
(không có chỉ mục) cho các truy vấn con, trái ngược với truy vấn ban đầu.
Đây là một thay đổi trò chơi có thể hữu ích cho nhiều người;
Tôi có một bảng có 200 nghìn hàng, với id tuần tự , tôi cần chọn N hàng ngẫu nhiên, vì vậy tôi chọn tạo các giá trị ngẫu nhiên dựa trên ID lớn nhất trong bảng, tôi đã tạo tập lệnh này để tìm ra hoạt động nhanh nhất:
logTime();
query("SELECT COUNT(id) FROM tbl");
logTime();
query("SELECT MAX(id) FROM tbl");
logTime();
query("SELECT id FROM tbl ORDER BY id DESC LIMIT 1");
logTime();
Kết quả là:
36.8418693542479
ms0.241041183472
ms0.216960906982
msDựa trên kết quả này, order desc là thao tác nhanh nhất để có được id tối đa,
đây là câu trả lời của tôi cho câu hỏi:
SELECT GROUP_CONCAT(n SEPARATOR ',') g FROM (
SELECT FLOOR(RAND() * (
SELECT id FROM tbl ORDER BY id DESC LIMIT 1
)) n FROM tbl LIMIT 10) a
...
SELECT * FROM tbl WHERE id IN ($result);
FYI: Để có được 10 hàng ngẫu nhiên từ một bảng 200 nghìn, tôi đã mất 1,78 ms (bao gồm tất cả các hoạt động ở phía php)
LIMIT
một chút - bạn có thể nhận được các bản sao.
Điều này là siêu nhanh và ngẫu nhiên 100% ngay cả khi bạn có khoảng trống.
x
hàng mà bạn có sẵnSELECT COUNT(*) as rows FROM TABLE
a_1,a_2,...,a_10
trong khoảng từ 0 đếnx
SELECT * FROM TABLE LIMIT 1 offset a_i
for i = 1, ..., 10Tôi tìm thấy bản hack này trong cuốn sách SQL Antipotypes từ Bill Karwin .
SELECT column FROM table ORDER BY RAND() LIMIT 10
nằm trong O (nlog (n)). Vì vậy, có, đây là giải pháp nhanh và nó hoạt động cho bất kỳ phân phối id.
x
. Tôi cho rằng đây không phải là thế hệ ngẫu nhiên gồm 10 hàng. Trong câu trả lời của tôi, bạn phải thực hiện truy vấn trong bước ba 10 lần, tức là chỉ có một hàng cho mỗi lần thực hiện và không phải lo lắng nếu phần bù nằm ở cuối bảng.
Kết hợp câu trả lời của @redsio với bảng tạm thời (600K là không nhiều):
DROP TEMPORARY TABLE IF EXISTS tmp_randorder;
CREATE TABLE tmp_randorder (id int(11) not null auto_increment primary key, data_id int(11));
INSERT INTO tmp_randorder (data_id) select id from datatable;
Và sau đó lấy phiên bản của @redsios Trả lời:
SELECT dt.*
FROM
(SELECT (RAND() *
(SELECT MAX(id)
FROM tmp_randorder)) AS id)
AS rnd
INNER JOIN tmp_randorder rndo on rndo.id between rnd.id - 10 and rnd.id + 10
INNER JOIN datatable AS dt on dt.id = rndo.data_id
ORDER BY abs(rndo.id - rnd.id)
LIMIT 1;
Nếu bàn lớn, bạn có thể rây ở phần đầu tiên:
INSERT INTO tmp_randorder (data_id) select id from datatable where rand() < 0.01;
Phiên bản: Bạn có thể giữ bảng tmp_randorder
liên tục, gọi nó là datitable_idlist. Tái tạo bảng đó trong các khoảng thời gian nhất định (ngày, giờ), vì nó cũng sẽ có lỗ. Nếu bàn của bạn trở nên thật to, bạn cũng có thể đổ đầy lỗ
chọn l.data_id làm toàn bộ từ datitable_idlist l trái tham gia dữ liệu dt trên dt.id = l.data_id trong đó dt.id là null;
Phiên bản: Cung cấp cho Dataset của bạn một cột Random_sortorder trực tiếp trong dữ liệu hoặc trong một bảng bổ sung liên tục datatable_sortorder
. Chỉ mục cột đó. Tạo giá trị ngẫu nhiên trong ứng dụng của bạn (tôi sẽ gọi nó $rand
).
select l.*
from datatable l
order by abs(random_sortorder - $rand) desc
limit 1;
Giải pháp này phân biệt các 'hàng cạnh' với Random_sortorder cao nhất và thấp nhất, do đó sắp xếp lại chúng theo các khoảng (một lần một ngày).
Một giải pháp đơn giản khác sẽ là xếp hạng các hàng và tìm nạp một trong số chúng một cách ngẫu nhiên và với giải pháp này, bạn sẽ không cần có bất kỳ cột 'Id' nào trong bảng.
SELECT d.* FROM (
SELECT t.*, @rownum := @rownum + 1 AS rank
FROM mytable AS t,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM mytable))) AS n
) d WHERE rank >= @cnt LIMIT 10;
Bạn có thể thay đổi giá trị giới hạn theo nhu cầu của bạn để truy cập nhiều hàng như bạn muốn nhưng đó chủ yếu sẽ là các giá trị liên tiếp.
Tuy nhiên, nếu bạn không muốn các giá trị ngẫu nhiên liên tiếp thì bạn có thể tìm nạp một mẫu lớn hơn và chọn ngẫu nhiên từ nó. cái gì đó như ...
SELECT * FROM (
SELECT d.* FROM (
SELECT c.*, @rownum := @rownum + 1 AS rank
FROM buildbrain.`commits` AS c,
(SELECT @rownum := 0) AS r,
(SELECT @cnt := (SELECT RAND() * (SELECT COUNT(*) FROM buildbrain.`commits`))) AS rnd
) d
WHERE rank >= @cnt LIMIT 10000
) t ORDER BY RAND() LIMIT 10;
Một cách mà tôi thấy khá tốt nếu có id được tạo tự động là sử dụng toán tử modulo '%'. Ví dụ: nếu bạn cần 10.000 bản ghi ngẫu nhiên trong số 70.000, bạn có thể đơn giản hóa việc này bằng cách nói rằng bạn cần 1 trên 7 hàng. Điều này có thể được đơn giản hóa trong truy vấn này:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0;
Nếu kết quả của việc chia các hàng mục tiêu cho tổng số có sẵn không phải là một số nguyên, bạn sẽ có thêm một số hàng so với những gì bạn yêu cầu, vì vậy bạn nên thêm một mệnh đề LIMIT để giúp bạn cắt tập kết quả như sau:
SELECT * FROM
table
WHERE
id %
FLOOR(
(SELECT count(1) FROM table)
/ 10000
) = 0
LIMIT 10000;
Điều này không yêu cầu quét toàn bộ, nhưng nó nhanh hơn ORDER BY RAND và theo tôi thì đơn giản hơn để hiểu hơn các tùy chọn khác được đề cập trong chủ đề này. Ngoài ra, nếu hệ thống ghi vào DB tạo ra các tập hợp theo lô, bạn có thể không nhận được kết quả ngẫu nhiên như bạn mong đợi.
Nếu bạn muốn một bản ghi ngẫu nhiên (không có vấn đề nếu có các khoảng cách giữa các id):
PREPARE stmt FROM 'SELECT * FROM `table_name` LIMIT 1 OFFSET ?';
SET @count = (SELECT
FLOOR(RAND() * COUNT(*))
FROM `table_name`);
EXECUTE stmt USING @count;
Tôi đã xem qua tất cả các câu trả lời, và tôi không nghĩ có ai đề cập đến khả năng này cả, và tôi không chắc tại sao.
Nếu bạn muốn sự đơn giản và tốc độ tối đa, với chi phí nhỏ, thì đối với tôi, việc lưu trữ một số ngẫu nhiên đối với mỗi hàng trong DB là điều hợp lý. Chỉ cần tạo một cột thêm random_number
và đặt mặc định thành RAND()
. Tạo một chỉ mục trên cột này.
Sau đó, khi bạn muốn lấy một hàng, hãy tạo một số ngẫu nhiên trong mã của bạn (PHP, Perl, bất cứ thứ gì) và so sánh nó với cột.
SELECT FROM tbl WHERE random_number >= :random LIMIT 1
Tôi đoán mặc dù nó rất gọn gàng cho một hàng, trong mười hàng như OP yêu cầu bạn phải gọi nó mười lần riêng biệt (hoặc đưa ra một tinh chỉnh thông minh thoát khỏi tôi ngay lập tức)
Sau đây phải nhanh, không thiên vị và độc lập với cột id. Tuy nhiên, điều đó không đảm bảo rằng số lượng hàng được trả về sẽ khớp với số lượng hàng được yêu cầu.
SELECT *
FROM t
WHERE RAND() < (SELECT 10 / COUNT(*) FROM t)
Giải thích: giả sử bạn muốn 10 hàng trong số 100 thì mỗi hàng có xác suất 1/10 để được CHỌN có thể đạt được WHERE RAND() < 0.1
. Cách tiếp cận này không đảm bảo 10 hàng; nhưng nếu truy vấn được chạy đủ số lần, số lượng hàng trung bình cho mỗi lần thực hiện sẽ vào khoảng 10 và mỗi hàng trong bảng sẽ được chọn đều.
PREPARE stm from 'select * from table limit 10 offset ?';
SET @total = (select count(*) from table);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Bạn cũng có thể áp dụng mệnh đề where như vậy
PREPARE stm from 'select * from table where available=true limit 10 offset ?';
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
EXECUTE stm using @_offset;
Đã thử nghiệm trên 600.000 hàng (700 MB) thực thi truy vấn bảng mất ~ 0,016 giây ổ đĩa cứng
--EDIT--
Phần bù có thể lấy một giá trị gần cuối bảng, điều này sẽ dẫn đến câu lệnh chọn trả về ít hàng hơn (hoặc có thể chỉ 1 hàng), để tránh điều này, chúng ta có thể kiểm tra offset
lại sau khi khai báo, như vậy
SET @rows_count = 10;
PREPARE stm from "select * from table where available=true limit ? offset ?";
SET @total = (select count(*) from table where available=true);
SET @_offset = FLOOR(RAND() * @total);
SET @_offset = (SELECT IF(@total-@_offset<@rows_count,@_offset-@rows_count,@_offset));
SET @_offset = (SELECT IF(@_offset<0,0,@_offset));
EXECUTE stm using @rows_count,@_offset;
Tôi sử dụng truy vấn này:
select floor(RAND() * (SELECT MAX(key) FROM table)) from table limit 10
thời gian truy vấn: 0,016s
Đây là cách tôi làm điều đó:
select *
from table_with_600k_rows
where rand() < 10/600000
limit 10
Tôi thích nó bởi vì không yêu cầu các bảng khác, nó đơn giản để viết và nó rất nhanh để thực hiện.
Sử dụng truy vấn đơn giản dưới đây để lấy dữ liệu ngẫu nhiên từ một bảng.
SELECT user_firstname ,
COUNT(DISTINCT usr_fk_id) cnt
FROM userdetails
GROUP BY usr_fk_id
ORDER BY cnt ASC
LIMIT 10
Tôi đoán đây là cách tốt nhất có thể ..
SELECT id, id * RAND( ) AS random_no, first_name, last_name
FROM user
ORDER BY random_no