2012-09-11 23 views
8

Cố gắng chọn một hàng ngẫu nhiên từ một bảng, dựa trên khóa chính được tự động tăng thêm không có lỗ.MySQL: Tại sao so sánh khóa chính với số được tạo ngẫu nhiên không sử dụng chỉ mục?

Giản đồ bảng:

CREATE TABLE IF NOT EXISTS `testTable` (
    `id` int(9) NOT NULL AUTO_INCREMENT, 
    `data` varchar(100) DEFAULT NULL, 
    PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=0 ; 

INSERT INTO `testTable` (`id`, `data`) VALUES 
(1, 'hello'), 
(2, 'world'), 
(3, 'new'), 
(4, 'data'), 
(5, 'more and more'), 
(6, 'data '), 
(7, 'more rows here'), 
(8, 'most rows here'), 
(9, 'testing'), 
(10,'last'); 

Queries:

1/explain select * from testTable where id = ceil(Rand()*10) limit 1 ;

http://sqlfiddle.com/#!2/6e2b1/1

Kết quả:

| ID | SELECT_TYPE |  TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS |  EXTRA | 
-------------------------------------------------------------------------------------------------------- 
| 1 |  SIMPLE | testTable | ALL |  (null) | (null) | (null) | (null) | 10 | Using where | 

2/explain select * from testTable where id = 7 limit 1 ;

http://sqlfiddle.com/#!2/6e2b1/2

Kết quả:

| ID | SELECT_TYPE |  TABLE | TYPE | POSSIBLE_KEYS |  KEY | KEY_LEN | REF | ROWS | EXTRA | 
--------------------------------------------------------------------------------------------------- 
| 1 |  SIMPLE | testTable | const |  PRIMARY | PRIMARY |  4 | const | 1 |  | 

Tại sao truy vấn # 1 không sử dụng các chỉ số, khi ceil(rand()*10) lý tưởng nên đánh giá để một hằng số mà sau đó có thể được so sánh với khóa chính? Không nên trình tối ưu hóa hoạt động theo cách đó? Hoặc tôi thiếu một cái gì đó hiển nhiên ở đây.

+3

sử dụng các hàm trong điều kiện WHERE (như ceil/rand) khiến hệ thống tìm nạp tất cả các hàng trước để có thể so sánh kết quả với mọi kết quả của hàm đó. Bạn sẽ được tốt hơn bằng cách sử dụng một "bên ngoài" chọn lấy giá trị số nguyên ngẫu nhiên và sau đó sử dụng này để lấy khóa chính của bạn. – Najzero

Trả lời

6

Không thể sử dụng khóa với truy vấn đó vì RAND() được gọi cho mỗi hàng và trả về một giá trị khác nhau mỗi lần.

Bạn có thể thử mã này thay vì:

SET @rand_value := CEIL(RAND()*10); 
EXPLAIN SELECT * FROM testTable WHERE id = @rand_value; 

Nó đầu tiên tính toán một giá trị ngẫu nhiên và gán nó vào một biến, sau đó sử dụng nó trong truy vấn.
Như được chỉ ra bởi tiểu hành tinh, LIMIT 1 là vô ích: vì điều kiện áp dụng cho khóa chính, truy vấn sẽ không bao giờ trả về nhiều hơn một hàng.

Với truy vấn này, đầu ra là:

| ID | SELECT_TYPE |  TABLE | TYPE | POSSIBLE_KEYS |  KEY | KEY_LEN | REF | ROWS | EXTRA | 
--------------------------------------------------------------------------------------------------- 
| 1 |  SIMPLE | testTable | const |  PRIMARY | PRIMARY |  4 | const | 1 |  | 
+2

+1 Ví dụ tốt cho việc sử dụng được đề xuất. Và trong trường hợp này vì 'id' là khóa chính nên không yêu cầu' LIMIT 1' :-) – aneroid

3

Từ MySQL documentation for RAND():

RAND() trong một điều khoản WHERE được tái đánh giá mỗi khi WHERE được thực hiện .

Vì vậy, không so sánh khóa chính với hằng số, đây là giá trị thay đổi mỗi lần (trong trường hợp này, cho mỗi hàng). Nếu bạn xóa LIMIT 1 trong truy vấn của mình, bạn sẽ thấy nhiều hàng hơn xuất hiện với các PK khác nhau được so khớp - hiển thị hành vi "được đánh giá lại mỗi lần".

Chỉnh sửa: Xem ví dụ của Jocelyn như một cách để tạo số ngẫu nhiên trước và sau đó nhận được một hàng có PK id phù hợp (yêu cầu LIMIT 1, btw). Tương tự như đã nêu trong bình luận của Najzero.

Các vấn đề liên quan