Melhor abordagem para "GOSTO OU GOSTO OU GOSTO OU GOSTO OU GOSTO"

10

Nesta questão, ele está tendo o mesmo problema que eu. Eu preciso de algo como:

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

Isso é feio e não está usando índices. Nesse caso, essa é realmente a única maneira de fazer isso (para selecionar várias palavras dentro de uma string) ou devo usar o FULLTEXT?

Pelo que entendi, com texto completo, posso selecionar várias palavras dentro de uma string.

Esta pergunta também fala sobre texto completo

Racer SQL
fonte
3
Qual é o tipo de dados da coluna do produto? Quantos caracteres em média?
Joe Obbish

Respostas:

17

Os índices de texto completo geralmente não são um item mágico e exigem manutenção adicional, espaço em disco e alterações bastante intrusivas nos padrões de consulta.

A menos que você realmente precise indexar documentos grandes (pense em corpos de email, PDFs, documentos do Word etc.), eles são um exagero (e, se for sincero, eu retiraria esse processo do SQL Server completamente e use Elasticsearch ou algo semelhante).

Para casos de uso menores, as colunas computadas geralmente são uma abordagem melhor.

Aqui está uma configuração de demonstração rápida:

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)

Consultar com base mesmo em uma coluna não persistente nos fornece um plano que 'usa índices' e tudo mais :)

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

NUTS

Erik Darling
fonte
-3

A resposta de sp_BlitzErik atinge muitos pontos positivos, mas acho que não é por isso que você não deve usar a Pesquisa de texto completo. A pesquisa de texto completo não existe para fazer o que você pensa que faz. Não existe para pesquisar vários campos. Ele existe para vetorizar o conteúdo de palavras e fazer uso de dicionários, stubbing, lexers, gazetteers, eliminação de palavras interrompidas e vários outros truques, dos quais nenhum se aplica. Ou ainda não foi mostrado para aplicar.

Também não concordo com a solução, embora não tenha certeza de como fazer isso melhor no SQL Server. Vamos recriar seus dados para o PostgreSQL - também é muito mais fácil criar no 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;

Agora, o que você deseja é um tipo de enumeração,

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

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

Agora você reduziu as seqüências de caracteres para representações inteiras. Mas melhor ainda, você pode consultá-los como antes.

SELECT *
FROM fulltextindexesarestupid
WHERE StopAbusingFeatures = 'Bad';

Isso tem o efeito.

  1. esconde o fato de que você é categorias são um tipo enumerado. Essa complexidade é encapsulada no tipo e oculta ao usuário.
  2. também coloca a manutenção nessas categorias no tipo.
  3. é padronizado.
  4. não aumenta o tamanho da linha.

Sem esses benefícios, você está basicamente tentando otimizar a comparação de cadeias. Mas, infelizmente, nem tenho certeza de como o sp_BlitzErik chega à resposta, conforme o código da sugestão,

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

Você pode reduzir os tokens para números inteiros usando uma enumeração, ou o método de rolagem manual sugerido por sp_BlitzErik, mas se você pode fazer o recolhimento, por que está fazendo o mesmo não-ancorado? Ou seja, se você sabe '% pasta%' é o símbolo 'pasta', por que você tem os %dois lados dela. Sem '%', isso é uma verificação de igualdade e deve ser muito rápida, mesmo como texto.

Evan Carroll
fonte