2012-09-11 3 views
8

Intentando seleccionar una fila aleatoria de una tabla, basada en la clave primaria autoincrementada sin agujeros.MySQL: ¿Por qué la comparación de la clave principal con un número generado aleatoriamente no utiliza el índice?

El esquema de la tabla:

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'); 

Consultas:

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

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

Resultado:

| 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

Resultado:

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

Por qué se consulta # 1 no utiliza el índice, cuando ceil(rand()*10) debe evaluar idealmente a una constante que puede ser comparada con la clave principal? ¿No debería el optimizador funcionar de esa manera? O me estoy perdiendo algo obvio aquí.

+3

utilizando funciones en DONDE las condiciones (como ceil/rand) hacen que el sistema busque todas las filas primero para poder comparar los resultados con cada resultado de esa función. Será mejor que utilice una selección "externa" para obtener el valor entero aleatorio y luego use esto para recuperar su clave principal. – Najzero

Respuesta

6

La clave no se puede usar con esa consulta porque se llama a RAND() para cada fila y devuelve un valor diferente cada vez.

Usted puede probar este código en su lugar:

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

En primer lugar, calcula un valor aleatorio y lo asigna a una variable, entonces se utiliza en la consulta.
Como lo señala aneroide, el LIMIT 1 es inútil: dado que la condición se aplica a la clave principal, la consulta nunca devolverá más de una fila.

Con esta consulta, la salida es:

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

+1 Buen ejemplo para el uso recomendado. Y en este caso, ya que 'id' es una clave primaria, por lo que no se requiere' LIMIT 1' :-) – aneroid

3

Desde el MySQL documentation for RAND():

RAND() en una cláusula WHERE se vuelve a evaluar cada vez que se ejecuta el WHERE .

Así no es comparar la clave principal con una constante, es un valor que cambia cada tiempo (en este caso, para cada fila). Si elimina el LIMIT 1 en su consulta, verá aparecer más filas con las diferentes PK coincidentes, lo que muestra el comportamiento "reevaluado cada vez".

Editar: Vea el ejemplo de Jocelyn como una forma de generar el número aleatorio primero y luego obtener una fila con el correspondiente PK id (el LIMIT 1 no es necesario, por cierto). De manera similar se afirma en el comentario de Najzero.

Cuestiones relacionadas