Meilleure approche pour "COMME OU COMME, OU COMME, OU COMME, OU COMME"


10

Dans cette question, il a le même problème que moi. J'ai besoin de quelque chose comme:

select * from blablabla 
where product 
like '%rock%' or
like '%paper%' or
like '%scisor%' or
like '%car%' or
like '%pasta%' 

C'est moche et cela n'utilise pas d'index. Dans ce cas, c'est vraiment la seule façon de le faire (pour sélectionner plusieurs mots dans une chaîne), ou dois-je utiliser FULLTEXT?

Si je comprends bien, avec le texte intégral, je peux sélectionner plusieurs mots dans une chaîne.

Cette question parle également du texte intégral


3
Quel est le type de données de la colonne produit? Combien de personnages en moyenne?
Joe Obbish

Réponses:


17

Les index de texte intégral ne sont généralement pas une solution miracle et nécessitent une maintenance supplémentaire, de l'espace disque et des modifications assez intrusives des modèles de requête.

À moins que vous n'ayez vraiment besoin d'indexer des documents volumineux (pensez aux corps d'e-mails, aux PDF, aux documents Word, etc.), ils sont excessifs (et si nous sommes honnêtes, je supprimerais complètement ce processus de SQL Server et utilisez Elasticsearch ou quelque chose de similaire).

Pour les cas d'utilisation plus petits, les colonnes calculées sont généralement une meilleure approche.

Voici une configuration de démonstration rapide:

use tempdb

CREATE TABLE #fulltextindexesarestupid (Id INT PRIMARY KEY CLUSTERED, StopAbusingFeatures VARCHAR(100))

INSERT #fulltextindexesarestupid (Id)
SELECT TOP 1000000 ROW_NUMBER() OVER (ORDER BY (@@ROWCOUNT))
FROM sys.messages AS m
CROSS JOIN sys.messages AS m2

UPDATE #fulltextindexesarestupid
SET StopAbusingFeatures = CASE WHEN Id % 15 = 0 THEN 'Bad'
                               WHEN Id % 3 = 0 THEN 'Idea'
                               WHEN Id % 5 = 0 THEN 'Jeans'
                               END


ALTER TABLE #fulltextindexesarestupid 
ADD LessBad AS CONVERT(BIT, CASE WHEN StopAbusingFeatures LIKE '%Bad%' THEN 1
                    WHEN StopAbusingFeatures LIKE '%Idea%' THEN 1
                    ELSE 0 END)

CREATE UNIQUE NONCLUSTERED INDEX ix_whatever ON #fulltextindexesarestupid (LessBad, Id)

L'interrogation basée sur même une colonne non persistante nous donne un plan qui «utilise des index» et tout :)

SELECT COUNT(*)
FROM #fulltextindexesarestupid AS f
WHERE LessBad = 1

DES NOISETTES


-3

La réponse de sp_BlitzErik touche de nombreux points positifs , mais je ne pense pas que ce soit la raison pour laquelle vous ne devriez pas utiliser la recherche en texte intégral. La recherche plein texte n'est pas là pour faire ce que vous pensez qu'elle fait. Il n'est pas là pour rechercher plusieurs champs. Il est là pour vectoriser le contenu des mots et utiliser des dictionnaires, du stubbing, des lexers, des répertoires géographiques, l'élimination des mots vides et une foule d'autres astuces qui ne s'appliquent pas. Ou, il n'a pas encore été démontré qu'il s'applique.

Je ne suis pas d'accord non plus avec la solution, mais je ne sais pas comment faire mieux dans SQL Server. Recréons ses données pour PostgreSQL - c'est aussi beaucoup plus propre de créer dans PostgreSQL.

CREATE TABLE fulltextindexesarestupid
AS
  SELECT
    id,
    CASE WHEN Id % 15 = 0 THEN 'Bad'
      WHEN Id % 3 = 0 THEN 'Idea'
      WHEN Id % 5 = 0 THEN 'Jeans'
    END AS StopAbusingFeatures
  FROM generate_series(1,1000000) AS id;

Maintenant, ce que vous voulez, c'est un type d'énumération,

CREATE TYPE foo AS ENUM ('Bad', 'Idea', 'Jeans');

ALTER TABLE fulltextindexesarestupid
  ALTER StopAbusingFeatures
  SET DATA TYPE foo
  USING StopAbusingFeatures::foo;

Vous avez maintenant réduit les chaînes en représentations entières. Mais encore mieux, vous pouvez les interroger comme avant.

SELECT *
FROM fulltextindexesarestupid
WHERE StopAbusingFeatures = 'Bad';

Cela a un effet.

  1. masque le fait que vos catégories sont un type énuméré. Cette complexité est encapsulée dans le type et cachée à l'utilisateur.
  2. il place également la maintenance sur ces catégories sur le type.
  3. c'est standardisé.
  4. il n'augmente pas la taille de la ligne.

Sans ces avantages, vous essayez essentiellement d'optimiser la comparaison des chaînes. Mais hélas, je ne sais même pas comment sp_BlitzErik parvient à la réponse compte tenu du code dans la suggestion,

like '%rock%' or
like '%paper%' or
like '%scisor%' or
like '%car%' or
like '%pasta%'

Vous pouvez réduire les jetons en nombres entiers à l'aide d'une énumération ou de la méthode de roulement à la main suggérée par sp_BlitzErik, mais si vous pouvez effectuer le regroupement, pourquoi faites-vous également le type non ancré? C'est-à-dire, si vous savez que «% pasta%» est le jeton «pasta», pourquoi en avez-vous des %deux côtés? Sans '%', c'est une vérification d'égalité et cela devrait être assez rapide même en tant que texte.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.