web-dev-qa-db-fra.com

Comment accélérer les requêtes SELECT .. LIKE dans MySQL sur plusieurs colonnes?

J'ai une table MySQL pour laquelle je fais très fréquemment des requêtes SELECT x, y, z FROM table WHERE x LIKE '%text%' OR y LIKE '%text%' OR z LIKE '%text%'. Un indice quelconque aiderait-il à accélérer les choses?

Il y a quelques millions d'enregistrements dans la table. Si quelque chose accélère la recherche, cela affectera-t-il sérieusement l'utilisation du disque par les fichiers de base de données et la vitesse des déclarations INSERT et DELETE? (aucune UPDATE n'est jamais effectuée)

Mettre à jour: Rapidement après avoir posté, j'ai vu beaucoup d'informations et de discussions sur la façon dont LIKE est utilisé dans la requête; Je tiens à souligner que la solution doit utilisez LIKE '%text%' (c’est-à-dire que le texte que je cherche est ajouté et précédé d’un caractère générique%). La base de données doit également être locale, pour de nombreuses raisons, notamment la sécurité.

36
Tom

Un index n'accélère pas la requête, car pour les colonnes textuelles, les index fonctionnent en indexant N caractères à partir de la gauche. Quand vous aimez LIKE '% text%', il ne peut pas utiliser l'index car il peut y avoir un nombre variable de caractères avant le texte.

Ce que vous devriez faire, ce n’est pas du tout utiliser une requête de ce type. Au lieu de cela, vous devriez utiliser quelque chose comme FTS (Full Text Search) que MySQL supporte pour les tables MyISAM. Il est également assez facile de créer ce système d’indexation vous-même pour les tables autres que MyISAM. Vous avez simplement besoin d’une table d’indexation séparée dans laquelle vous stockez les mots et leurs identifiants pertinents dans la table.

59
reko_t

Un index ne facilitera pas la correspondance de texte avec un caractère générique de premier plan, un index peut être utilisé pour:

LIKE 'text%'

Mais je suppose que ça ne va pas le couper. Pour ce type de requête, vous devriez vraiment faire appel à un fournisseur de recherche en texte intégral si vous souhaitez mettre à l'échelle la quantité d'enregistrements que vous pouvez rechercher. Mon fournisseur préféré est Sphinx , très complet/rapide, etc. Lucene pourrait également valoir le coup d'oeil. Un index de texte intégral sur une table MyISAM fonctionnera également, mais poursuivre en dernier recours sur MyISAM pour toute base de données contenant beaucoup d'écritures n'est pas une bonne idée.

18
Michael

Un index peut non être utilisé pour accélérer les requêtes lorsque les critères de recherche commencent par un caractère générique:

LIKE '%text%'

Un index peut (et peut être, en fonction de la sélectivité) utilisé pour les termes de recherche de la forme: 

LIKE 'text%'

12
Mitch Wheat

Peut-être que vous pouvez essayer de mettre à jour mysql5.1 vers mysql5.7.

J'ai environ 70 000 enregistrements. Et exécutez SQL suivant:

select * from comics where name like '%test%'; 

Cela prend 2000ms dans mysql5.1 . Et cela prend 200ms dans mysql5.7 ou mysql5.6.

7
lingceng

Autrement:

Vous pouvez conserver les colonnes calculées avec ces chaînes REVERSEd et utiliser

SELECT x, y, z FROM table WHERE x LIKE 'text%' OR y LIKE 'text%' OR z LIKE 'text%' OR xRev LIKE 'txet%' OR yRev LIKE 'txet%' OR zRev LIKE 'txet%' 

Exemple de comment ajouter une colonne persistante stockée

ALTER TABLE table ADD COLUMN xRev VARCHAR(N) GENERATED ALWAYS AS REVERSE(x) stored;

puis créez un index sur xRev, yRev etc.

Une autre alternative pour éviter les analyses de table complètes consiste à sélectionner des sous-chaînes et à les vérifier dans l'instruction having:

SELECT 
    al3.article_number,
    SUBSTR(al3.article_number, 2, 3) AS art_nr_substr,
    SUBSTR(al3.article_number, 1, 3) AS art_nr_substr2,
    al1.*
FROM
    t1 al1 
    INNER JOIN t2 al2 ON al2.t1_id = al1.id
    INNER JOIN t3 al3 ON al3.id = al2.t3_id
WHERE
    al1.created_at > '2018-05-29'
HAVING 
    (art_nr_substr = "FLA" OR art_nr_substr = 'VKV' OR art_nr_subst2 = 'PBR');
0
Juri Sinitson