Approccio migliore per "MI PIACE O MI PIACE, O MI PIACE, O MI PIACE O MI PIACE"


10

In questa domanda ha lo stesso problema. Ho bisogno di qualcosa del tipo:

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

Questo è brutto e non usa gli indici. In questo caso, questo è davvero l'unico modo per farlo (per selezionare più parole all'interno di una stringa), o dovrei usare FULLTEXT?

A quanto ho capito, con il testo completo, posso selezionare più parole all'interno di una stringa.

Questa domanda parla anche di Full Text


3
Qual è il tipo di dati della colonna del prodotto? Quanti personaggi in media?
Joe Obbish il

Risposte:


17

Gli indici di testo completo generalmente non sono un proiettile magico e richiedono manutenzione aggiuntiva, spazio su disco e modifiche abbastanza invasive ai modelli di query.

A meno che tu non abbia davvero bisogno di indicizzare documenti di grandi dimensioni (pensa a corpi e-mail, PDF, documenti Word, ecc.), Sono eccessivi (e se siamo onesti, toglierei completamente questo processo da SQL Server e usa Elasticsearch o qualcosa di simile).

Per casi d'uso più piccoli, le colonne calcolate sono generalmente un approccio migliore.

Ecco una rapida configurazione demo:

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)

Le query basate anche su una colonna non persistente ci danno un piano che "usa gli indici" e tutto :)

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

NOCCIOLINE


-3

sp_BlitzLa risposta di Erik colpisce molti buoni punti, ma non credo sia per questo che non dovresti usare la ricerca full-text. La ricerca full-text non è lì per fare ciò che pensi che faccia. Non è lì per cercare più campi. È lì per vettorializzare il contenuto delle parole e fare uso di dizionari, mozziconi, lexer, dizionari geografici, eliminazione di parole in blocco e una serie di altri trucchi che non si applicano. Oppure, non è stato ancora dimostrato di applicare.

Non sono d'accordo con la soluzione, anche se non sono sicuro di come farlo meglio in SQL Server. Ricreamo i suoi dati per PostgreSQL - è molto più pulito creare anche in 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;

Ora quello che vuoi è un tipo enum,

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

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

Ora hai compresso le stringhe in rappresentazioni di numeri interi. Ma ancora meglio puoi interrogarli come prima.

SELECT *
FROM fulltextindexesarestupid
WHERE StopAbusingFeatures = 'Bad';

Questo ha l'effetto.

  1. nasconde il fatto che le categorie sono di tipo elencato. Tale complessità è incapsulata nel tipo e nascosta all'utente.
  2. pone anche la manutenzione su quelle categorie sul tipo.
  3. è standardizzato.
  4. non aumenta la dimensione della riga.

Senza questi vantaggi, essenzialmente stai solo cercando di ottimizzare il confronto delle stringhe. Ma ahimè, non sono nemmeno sicuro di come sp_BlitzErik arriva alla risposta dato il codice nel suggerimento,

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

Puoi comprimere i token in numeri interi usando un enum, o il metodo di rotolamento manuale suggerito da sp_BlitzErik ma se riesci a fare il collasso perché stai facendo anche il tipo non ancorato? Vale a dire, se sai che '% pasta%' è il token 'pasta', perché ne hai %entrambi i lati. Senza '%' questo è un controllo di uguaglianza e dovrebbe essere abbastanza veloce anche come testo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.