Domanda

Mi chiedo come sarebbe il rendimento di una query usando la parola chiave LIKE e il carattere jolly come valore rispetto al non avere alcuna clausola where.

Prendi in considerazione una clausola where come " WHERE A LIKE '%' " ;. Ciò corrisponderà a tutti i possibili valori della colonna "a". Come si confronta con il non avere affatto la clausola where.

Il motivo per cui lo chiedo è che ho un'applicazione in cui ci sono alcuni campi in cui l'utente può specificare i valori su cui cercare. In alcuni casi l'utente vorrebbe tutti i possibili risultati. Attualmente sto usando una singola query come questa:

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ?

I valori di '%' e '%' possono essere forniti in modo che corrispondano a tutti i valori possibili per a e o b. Questo è utile poiché posso utilizzare una singola query con nome nella mia applicazione per questo. Mi chiedo quali siano le considerazioni sulle prestazioni per questo. Query Optimizer riduce LIKE '%' per abbinare semplicemente tutti? Mi rendo conto che, poiché sto utilizzando una query denominata (istruzione preparata), ciò potrebbe influire anche sulla risposta. Mi rendo conto che la risposta è probabilmente specifica del database. Quindi, nello specifico, come funzionerebbe in Oracle, MS SQL Server e Derby.

L'approccio alternativo a questo sarebbe quello di utilizzare 3 query separate in base all'utente che immette il carattere jolly.

A è una query jolly:

SELECT * FROM TableName WHERE b LIKE ?

B è una query jolly:

SELECT * FROM TableName WHERE a LIKE ?

A e B sono caratteri jolly:

SELECT * FROM TableName

Nessun carattere jolly:

<*>

Ovviamente avere una sola query è il più semplice e facile da mantenere. Preferirei usare solo una query se le prestazioni saranno ancora buone.

È stato utile?

Soluzione 3

Speravo che ci sarebbe stata una risposta da manuale a questo, ma sembra che varierà notevolmente con diversi tipi di database. La maggior parte delle risposte ha indicato che dovrei eseguire un test in modo che sia esattamente quello che ho fatto.

La mia applicazione è rivolta principalmente ai database Derby, MS SQL e Oracle. Poiché il derby può essere eseguito incorporato ed è facile da configurare, ho testato prima le prestazioni. I risultati sono stati sorprendenti. Ho testato lo scenario peggiore su un tavolo abbastanza grande. Ho eseguito il test 1000 volte e ho calcolato la media dei risultati.

Query 1:

SELECT * FROM TableName

Query 2 (con valori di a = "% " e b = "% "):

SELECT * FROM TableName WHERE a LIKE ? AND b LIKE ?

Tempo medio query 1: 178 ms

Tempo medio query 2: 181 ms

Quindi le prestazioni sul derby sono quasi le stesse tra le due query.

Altri suggerimenti

SQL Server generalmente vedrà

WHERE City LIKE 'A%'

e trattalo come

WHERE City >= 'A' AND City < 'B'

... e usa felicemente un indice di ricerca se appropriato. Dico "in generale", perché ho visto che in alcuni casi non riesce a semplificare.

Se qualcuno sta cercando di fare:

WHERE City LIKE '%ville'

... quindi una ricerca di indici sarà sostanzialmente impossibile.

Ma qualcosa di semplice come:

WHERE City LIKE '%'

sarà considerato equivalente a:

WHERE City IS NOT NULL

Puoi utilizzare qualsiasi analisi di query offerta da DBMS (ad es. EXPLAIN per MySQL, IMPOSTA SHOWPLAN_ALL ON per MS SQL (o usa uno dei altri metodi ), EXPLAIN PLAN FOR per Oracle) per vedere come verrà eseguita la query.

Qualsiasi DBMS degno di nota eliminerebbe le clausole LIKE '%' prima ancora di provare a eseguire la query. Sono abbastanza sicuro di aver visto DB2 / z fare questo nei suoi piani di esecuzione.

L'istruzione preparata non dovrebbe fare la differenza poiché dovrebbe essere trasformata in reale SQL prima di arrivare al motore di esecuzione.

Ma, come per tutte le domande di ottimizzazione, misura, non indovinare ! I DBA esistono perché ottimizzano costantemente il DBMS in base ai dati effettivi (che cambiano nel tempo). Come minimo, dovresti avere il tempo (e ottenere i piani di esecuzione) per tutte le varianti con dati statici adeguati per vedere se c'è una differenza.

So che query come:

select c from t where ((1 = 1) or (c = ?))

sono ottimizzati per rimuovere l'intera clausola where prima dell'esecuzione (su DB2 comunque e, prima di chiedere, il costrutto è utile dove è necessario rimuovere l'effetto della clausola where ma mantenere comunque il parametro segnaposto (usando BIRT con Javascript per modificare le query per i caratteri jolly).

Derby offre anche strumenti per esaminare il piano di query effettivo utilizzato, in modo da poter eseguire esperimenti utilizzando Derby e osservare il piano di query che Derby ha scelto. È possibile eseguire Derby con -Dderby.language.logQueryPlan = true e Derby scriverà il piano di query su derby.log oppure è possibile utilizzare la funzione RUNTIMESTATISTICS, come descritto qui: http://db.apache.org/derby/docs/10.5/tuning/ctundepth853133.html

Non sono sicuro che il Derby eliminerà anticipatamente A LIKE '%', ma non penso anche che la presenza di quella clausola introdurrà molto di un rallentamento della velocità di esecuzione.

Sarei piuttosto interessato a vedere l'output del piano di query effettivo che ottieni nel tuo ambiente, con e senza la clausola A LIKE '%' in atto.

Oracle 10gR2 non sembra eseguire un'ottimizzazione speciale per questa situazione, ma riconosce che LIKE '%' esclude i null.

create table like_test (col1)
as select cast(dbms_random.string('U',10) as varchar2(10))
from dual
connect by level <= 1000
/
insert into like_test values (null)
/
commit
/

exec dbms_stats.gather_table_stats(user,'like_test')

explain plan for
select count(*)
from   like_test
/
select plan_table_output from table(dbms_xplan.display)
/
explain plan for
select count(*)
from   like_test
where  col1 like '%'
/
select plan_table_output from table(dbms_xplan.display)
/
explain plan for
select count(*)
from   like_test
where  col1 is not null
/
select plan_table_output from table(dbms_xplan.display)
/

... dare ...

Plan hash value: 3733279756

------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Cost (%CPU)| Time     |
------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |            |          |
|   2 |   TABLE ACCESS FULL| LIKE_TEST |  1001 |     3   (0)| 00:00:01 |
------------------------------------------------------------------------

... e ...

Plan hash value: 3733279756

--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    10 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |    10 |            |          |
|*  2 |   TABLE ACCESS FULL| LIKE_TEST |  1000 | 10000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("COL1" LIKE '%')

... e ...

Plan hash value: 3733279756

--------------------------------------------------------------------------------
| Id  | Operation          | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |           |     1 |    10 |     3   (0)| 00:00:01 |
|   1 |  SORT AGGREGATE    |           |     1 |    10 |            |          |
|*  2 |   TABLE ACCESS FULL| LIKE_TEST |  1000 | 10000 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("COL1" IS NOT NULL)

Nota la cardinalità (righe) sulla riga TABLE ACCESS FULL

A seconda di come è strutturato il predicato LIKE e del campo su cui stai testando, potresti aver bisogno di una scansione completa della tabella. Semanticamente una '%' potrebbe implicare una scansione completa della tabella, ma Sql Server esegue internamente ogni tipo di ottimizzazione sulle query. Quindi la domanda diventa: Sql Server si ottimizza su un predicato LIKE formato con '%' e lo butta fuori dalla clausola WHERE?

Un aspetto che ritengo manchi dalla discussione è il fatto che l'OP vuole usare una dichiarazione preparata. Al momento della preparazione della dichiarazione, il database / ottimizzatore non sarà in grado di elaborare le semplificazioni menzionate da altri e quindi non sarà in grado di ottimizzare come '%' come l'attuale il valore non sarà noto al momento della preparazione.

Quindi:

  • quando si utilizzano istruzioni preparate, sono disponibili quattro diverse istruzioni (0, solo a, solo b, entrambe) e utilizzare quella appropriata quando necessario
  • vedi se ottieni prestazioni migliori quando non usi un'istruzione preparata quando ti attieni a una sola istruzione (anche se allora sarebbe abbastanza facile non includere condizioni "vuote")

Cosa succede se una colonna ha un valore vuoto non nullo? La tua query probabilmente corrisponderà.

Se questa è una query per un'applicazione del mondo reale, prova a utilizzare le funzionalità di indicizzazione del testo libero della maggior parte dei moderni database sql. I problemi di prestazioni diventeranno insignificanti.

Una semplice dichiarazione if di if (A B)  cerca a b altro (A)  cerca a altro B  ricerca b altro  dire all'utente che non hanno specificato nulla

è banale da mantenere e diventa molto più facile da capire invece di fare ipotesi sull'operatore LIKE. Probabilmente lo farai comunque nell'interfaccia utente quando visualizzi i risultati "La tua ricerca di A trovato x" oppure " La tua ricerca di A B trovata ... "

Non sono sicuro del valore di usare un'istruzione preparata con il tipo di parametri che stai descrivendo. Il motivo è che potresti ingannare Query Optimizer nel preparare un piano di esecuzione che sarebbe completamente sbagliato a seconda di quale dei parametri fosse '%'.

Ad esempio, se l'istruzione è stata preparata con un piano di esecuzione utilizzando l'indice sulla colonna A, ma il parametro per la colonna A risulta essere '%', si potrebbero verificare prestazioni scarse.

una clausola where con " come "%" " poiché l'unico predicato si comporterà esattamente come una clausola where.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top