2009-10-14 16 views
9

Ho una tabella con circa 117000 registrazioni. Devo eseguire una ricerca che controlli 3 campi separati per un dato motivo di stringa.Come posso ottimizzare/rifattorizzare una clausola "LIKE" di TSQL?

mio in cui la clausola è la seguente:

field1 LIKE '%' + @DESC + '%' 
OR field2 LIKE '%' + @DESC + '%' 
OR field3 LIKE '%' + @DESC + '%' 

Questo sembra prendere circa 24 secondi, indipendentemente ingresso ...

C'è un modo migliore per fare questo? Meno di 10 (o 5!) Secondi sarebbero molto più preferibili.

Grazie per qualsiasi aiuto.

risposta

14

Utilizzare Ricerca testo completo e CONTAINS. LIKE non può essere ottimizzato durante la ricerca nel mezzo del campo, ad es. quando l'espressione LIKE inizia con un '%', quindi eseguirà sempre una scansione completa della tabella.

+0

è possibile usa un indice, con un po 'di lavoro, quando fai LIKE'% '+ string, vedi il link nel mio altro commento. –

+0

@KM: trucco interessante, una colonna invertita. Anche una buona analogia con la rubrica telefonica rende chiaramente chiaro il caso. –

+0

bel trucco, per obad non c'è nulla di simile per il problema del tipo '% text%'. – HLGEM

1

Ogni volta che si avvia una ricerca LIKE con un carattere jolly, si esegue una scansione. A meno che non sia possibile restringere i criteri di ricerca per includere il primo carattere (che potrebbe non essere fattibile), è necessario ricorrere alla ricerca di testo completo.

+3

@Stuart Ainsworth ha detto _Ancora che si avvia una ricerca LIKE con un carattere jolly, si sta eseguendo una scansione_, che non deve essere vero, vedere questo: http://stackoverflow.com/questions/1388059/sql-server-index -columns-used-in-like/1395881 # 1395881 –

+0

Ora stai solo giocando con la semantica; REVERSI la colonna e indicizzala, quindi inverti la clausola like in modo che non inizi con un carattere jolly. Non stai iniziando una ricerca con un carattere jolly, quindi non stai facendo una scansione. Ammetto che è una soluzione interessante, e dovrò tenerlo a mente. –

0

come su

field1 + field2 + field3 LIKE '%' + @DESC + '%' 

o

CONTAINS(field1 + field2 + field3, @DESC) 
1

Avete davvero bisogno di iniziare con un carattere jolly? Perché? Spesso puoi costringere gli utenti a digitare almeno il primo carattere. Ne parlo perché alcuni sviluppatori usano semplicemente il carattere jolly come abitudine perché non è necessario. Nella maggior parte dei casi, gli utenti saranno in grado di digitare il primo carattere, a meno che il file non memorizzi le stringhe lunghe (ad esempio i nomi degli aeroporti ufficiali). Altrimenti è davvero necessario utilizzare l'indicizzazione full-text anche se il trucco di KM con il contrario è piuttosto interessante se non si ha bisogno del carattere jolly alla fine.

Se si riesce a evitare di uccidere le prestazioni, fare ciò.

+0

Sì, ho bisogno di un carattere jolly a doppia faccia per questa query. Ho bisogno di trovare qualcosa come "banana" in "Strawberry Banana Yogurt". – IronicMuffin

1

Mentre sono d'accordo con la risposta accettata che completa indicizzazione di testo sarebbe la soluzione migliore e sono in alcun modo a favore dell'uso di condurre ricerche con caratteri jolly se devono essere eseguite poi ci sono potenziali misure che possono essere adottate per rendere le prestazioni di loro meno cattive.

Kalen Delaney nel libro "Microsoft SQL Server 2008 Internals" dice:

fascicolazione può fare una grande differenza quando SQL Server deve esaminare quasi tutti i caratteri nelle stringhe. Per esempio, guardare il seguente:

SELECT COUNT(*) FROM tbl WHERE longcol LIKE '%abc%' 

Questo può eseguire 10 volte più veloce o più con un confronto binario di un confronto non binari di Windows. E con i dati varchar, questo viene eseguito fino a sette o otto volte più velocemente con un confronto SQL rispetto alle regole di confronto di Windows.

+0

In che modo le regole di confronto influiscono sulle prestazioni? – crosenblum

+0

Ulteriori informazioni dal libro "Se si dispone di una colonna varchar, è possibile accelerare forzando le regole di confronto nel modo seguente: SELECT COUNT (*) FROM tbl WHERE longcol COLLATE SQL_Latin1_General_CP_CI_AS LIKE '% abc%';" –

+0

@yoelhalb probabilmente intendevi '_CP1_' not' _CP_' – jazzcat

0

Ho provato una possibile soluzione. Prima di questa soluzione, anche la query non restituiva risultati e causava errori di timeout della connessione.

La mia query stava avendo il filtro della data e altri criteri.Tutti gli altri criteri erano come la ricerca. Una parola chiave di colonna stava cercando come '% abc%' sulla colonna ntext e stava facendo una scansione completa della tabella.

Soluzione:

interrogazione Divide in 2 parti. 1) Prima parte in CTE (Common Table Express) 2) Applicare tutti i criteri di ricerca su CTE.

WITH SearchData(Column1,Column2,Column3,Column4,........) 
    AS 
    (
    SELECT Column1,Column2,Column3,Column4,........... 
    FROM myTable1 WITH(NOLOCK) 
      INNER JOIN MyTable2 WITH(NOLOCK) 
       ON MyTable1.id = MyTable2.Id 
    WHERE (MyTable1.CreationTime >= '2014-04-27' AND MyTable1.CreationTime <= '2014-05-01') 
) 

    SELECT DISTINCT top 250 Column1,Column2,Column3,Column4 
    FROM SearchData 
    WHERE (ISNULL(Column1,'') LIKE @Column1 +'%' OR @Column1 IS NULL) 
      and (Column2 LIKE @Column2+ '%' OR @Column2 IS NULL) 
      ... 
      ... 
      ... 
      ... 
      AND (Column10 like '%'[email protected]+'%' or @Column10 IS NULL) 
      AND @[email protected][email protected][email protected] <> '' 
      ORDER BY [CreationTime] DESC 

Ha funzionato per me.

+0

Penso che usare la clausola 'TOP' con una query che ha una condizione LIKE aumenterà sempre le prestazioni. Quindi è la clausola TOP piuttosto che una CTE che contribuisce al guadagno di prestazioni, poiché con TOP una scansione completa viene interrotta non appena viene scansionato il numero richiesto di record. – Sunil

+1

come nota fuori tema usando WITH (NOLOCK) in ogni join è molto pericoloso devi essere molto attento con esso o otterrai dati duplicati, ecc ... Vedo le persone che pubblicizzano questo suggerimento come un modo standard per ottimizzare le query, non.dovresti sempre pensarci due volte e rivedere il modo in cui le tabelle vengono scritte (inserite/aggiornate) nella tua applicazione prima di lanciare un suggerimento nolock. (solo dicendo) –

0

Se non è possibile utilizzare FullTextSearch, è possibile aumentare la velocità in 10 volte. Operazioni successive:

1 Add campo calcolato: Indice

alter table TableName 
add CalculatedColumnName as upper(Column1 + '|' + Column2...) collate Latin1_General_100_Bin2 
persisted; 

2 Aggiungi per campo calcolato:

create nonclustered index IDX_TableName_CalculatedColumnName 
on TableName(CalculatedColumnName); 

3 Modificare il testo della query

select count(*) 
from TableName 
where CalculatedColumnName like '%' + upper(@ParameterValue) + '%' collate Latin1_General_100_Bin2 

Fonte: http://aboutsqlserver.com/2015/01/20/optimizing-substring-search-performance-in-sql-server

Problemi correlati