Mysql
 sql >> Base de données >  >> RDS >> Mysql

MySQL FIND_IN_SET ou équivalent peut-il être fait pour utiliser des index ?

En référence à votre commentaire :

@MarcB la base de données est normalisée, la chaîne CSV provient de l'UI."Obtenez-moi les données pour les personnes suivantes :101,202,303"

Cette réponse se concentre uniquement sur les chiffres séparés par une virgule. Parce qu'il s'avère que vous ne parliez même pas de FIND_IN_SET après tout.

Oui, vous pouvez obtenir ce que vous voulez. Vous créez une instruction préparée qui accepte une chaîne comme paramètre comme dans cette Recent Answer à moi. Dans cette réponse, regardez le deuxième bloc qui montre la CREATE PROCEDURE et son 2ème paramètre qui accepte une chaîne comme (1,2,3) . Je reviendrai sur ce point dans un instant.

Pas que vous ayez besoin de le voir @spraff mais d'autres pourraient le faire. La mission est d'obtenir le type !=TOUT, et possible_keys et keys d'Expliquer pour ne pas afficher null, comme vous l'avez montré dans votre deuxième bloc. Pour une lecture générale sur le sujet, consultez l'article Comprendre Sortie d'EXPLAIN et la page du manuel MySQL intitulée EXPLAIN Informations supplémentaires .

Maintenant, revenons au (1,2,3) référence ci-dessus. Nous savons d'après votre commentaire et votre deuxième résultat d'explication dans votre question qu'il atteint les conditions souhaitées suivantes :

  1. type =range (et en particulier pas ALL) . Voir les documents ci-dessus à ce sujet.
  2. la clé n'est pas nulle

Ce sont précisément les conditions que vous avez dans votre deuxième sortie EXPLAIN, et la sortie qui peut être vue avec la requête suivante :

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

où j'ai 999 valeurs dans ce in liste de clauses. C'est un exemple de cette réponse de la mienne dans l'annexe D qui génère une telle chaîne aléatoire de csv, entourée de parenthèses ouvrantes et fermantes.

Et notez la sortie d'explication suivante pour cet élément 999 dans la clause ci-dessous :

Objectif atteint. Vous y parvenez avec un proc stocké similaire à celui que j'ai mentionné précédemment dans ce lien en utilisant une PREPARED STATEMENT (et ces choses utilisent concat() suivi d'un EXECUTE ).

L'index est utilisé, un Tablescan (signifiant mauvais) n'est pas expérimenté. D'autres lectures sont La plage Join Type , toute référence que vous pouvez trouver sur l'optimiseur basé sur les coûts (CBO) de MySQL, cette réponse de vladr bien que daté, avec un œil sur le ANALYZE TABLE partie, en particulier après des modifications importantes des données. Notez que ANALYZE peut prendre beaucoup de temps pour s'exécuter sur des ensembles de données ultra-énormes. Parfois plusieurs heures.

Attaques par injection SQL :

L'utilisation de chaînes transmises aux procédures stockées est un vecteur d'attaque pour les attaques par injection SQL. Des précautions doivent être mises en place pour les éviter lors de l'utilisation de données fournies par l'utilisateur. Si votre routine est appliquée à vos propres identifiants générés par votre système, vous êtes en sécurité. Notez, cependant, que les attaques par injection SQL de 2e niveau se produisent lorsque des données ont été mises en place par des routines qui n'ont pas nettoyé ces données lors d'une insertion ou d'une mise à jour précédente. Attaques mises en place en amont via les données et utilisées plus tard (sorte de bombe à retardement).

Cette réponse est donc Terminée pour la plupart.

Le tableau ci-dessous est une vue du même tableau avec une modification mineure pour montrer ce qu'est un tablescan redouté ressemblerait à la requête précédente (mais par rapport à une colonne non indexée appelée thing ).

Consultez notre définition de table actuelle :

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 |
+---------+---------+----------+

Notez que la colonne thing était une colonne int nullable auparavant.

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;

Et puis l'Expliquer qui est un Tablescan (contre la colonne thing ):