sql >> Base de Datos >  >> RDS >> Mysql

¿Se puede hacer MySQL FIND_IN_SET o equivalente para usar índices?

En referencia a su comentario:

@MarcB, la base de datos está normalizada, la cadena CSV proviene de la interfaz de usuario. "Obtén los datos de las siguientes personas:101 202 303"

Esta respuesta tiene un enfoque limitado solo en esos números separados por una coma. Porque resulta que ni siquiera estabas hablando de FIND_IN_SET después de todo.

Sí, puedes lograr lo que quieras. Crea una declaración preparada que acepta una cadena como parámetro como en esta Reciente respuesta mío. En esa respuesta, mire el segundo bloque que muestra el CREATE PROCEDURE y su segundo parámetro que acepta una cadena como (1,2,3) . Volveré a este punto en un momento.

No es que necesites verlo @spraff, pero otros podrían hacerlo. La misión es conseguir el type !=TODO, y possible_keys y keys de Explicar para no mostrar nulo, como mostraste en tu segundo bloque. Para obtener una lectura general sobre el tema, consulte el artículo Comprensión Salida de EXPLAIN y la página del manual de MySQL titulada EXPLAIN Información adicional .

Ahora, volvamos al (1,2,3) referencia arriba. Sabemos por su comentario y su segundo resultado de Explicación en su pregunta que cumple las siguientes condiciones deseadas:

  1. tipo =rango (y en particular no TODOS) . Consulte los documentos anteriores sobre esto.
  2. la clave no es nula

Estas son precisamente las condiciones que tiene en su segundo resultado de Explicación, y el resultado que se puede ver con la siguiente consulta:

explain 
select * from ratings where id in (2331425, 430364, 4557546, 2696638, 4510549, 362832, 2382514, 1424071, 4672814, 291859, 1540849, 2128670, 1320803, 218006, 1827619, 3784075, 4037520, 4135373, ... use your imagination ..., ...,  4369522, 3312835);

donde tengo 999 valores en ese in lista de cláusulas. Esa es una muestra de esta respuesta mío en el Apéndice D que genera una cadena tan aleatoria de csv, rodeada de paréntesis abiertos y cerrados.

Y tenga en cuenta el siguiente resultado de Explicación para ese elemento 999 en la cláusula a continuación:

Objetivo logrado. Esto se logra con un proceso almacenado similar al que mencioné antes en este enlace usando un PREPARED STATEMENT (y esas cosas usan concat() seguido de EXECUTE ).

Se utiliza el índice, no se experimenta un Tablescan (que significa malo). Lecturas adicionales son The range Join Type , cualquier referencia que pueda encontrar en el Optimizador basado en costos (CBO) de MySQL, esta respuesta de vladr aunque fechado, con un ojo en el ANALYZE TABLE parte, en particular después de cambios significativos en los datos. Tenga en cuenta que ANALYZE puede tardar mucho tiempo en ejecutarse en conjuntos de datos ultragrandes. A veces, muchas, muchas horas.

Ataques de inyección Sql:

El uso de cadenas pasadas a procedimientos almacenados es un vector de ataque para ataques de inyección SQL. Se deben tomar precauciones para evitarlos cuando se utilizan datos proporcionados por el usuario. Si su rutina se aplica contra su propia identificación generada por su sistema, entonces está a salvo. Tenga en cuenta, sin embargo, que los ataques de inyección SQL de segundo nivel ocurren cuando los datos fueron colocados por rutinas que no desinfectaron esos datos en una inserción o actualización anterior. Ataques implementados previamente a través de datos y utilizados posteriormente (una especie de bomba de relojería).

Entonces esta respuesta está Terminada en su mayor parte.

La siguiente es una vista de la misma tabla con una modificación menor para mostrar lo que es un temido Tablescan se vería como en la consulta anterior (pero contra una columna no indexada llamada thing ).

Eche un vistazo a nuestra definición de tabla actual:

CREATE TABLE `ratings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `thing` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5046214 DEFAULT CHARSET=utf8;

select min(id), max(id),count(*) as theCount from ratings;
+---------+---------+----------+
| min(id) | max(id) | theCount |
+---------+---------+----------+
|       1 | 5046213 |  4718592 |
+---------+---------+----------+

Tenga en cuenta que la columna thing antes era una columna int anulable.

update ratings set thing=id where id<1000000;
update ratings set thing=id where id>=1000000 and id<2000000;
update ratings set thing=id where id>=2000000 and id<3000000;
update ratings set thing=id where id>=3000000 and id<4000000;
update ratings set thing=id where id>=4000000 and id<5100000;
select count(*) from ratings where thing!=id;
-- 0 rows

ALTER TABLE ratings MODIFY COLUMN thing int not null;

-- current table definition (after above ALTER):
CREATE TABLE `ratings` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `thing` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5046214 DEFAULT CHARSET=utf8;

Y luego Explique que es un Tablescan (contra la columna thing ):