Domanda

Se ho una domanda del tipo:

Select EmployeeId 
From Employee 
Where EmployeeTypeId IN (1,2,3)

e ho un indice su EmployeeTypeId campo, SQL Server utilizza ancora quell'indice?

È stato utile?

Soluzione

Si, è esatto.Se la tabella dei dipendenti ha 10.000 record e solo 5 record hanno EmployeetypeID in (1,2,3), molto probabilmente utilizzerà l'indice per recuperare i record.Tuttavia, se rileva che 9.000 record hanno l'employeeIDType in (1,2,3), molto probabilmente eseguirà semplicemente una scansione della tabella per ottenere gli EmployeeID corrispondenti, poiché è più veloce semplicemente scorrere l'intera tabella piuttosto che andare a ogni ramo dell'albero dell'indice ed esaminare i record individualmente.

SQL Server fa molte cose per provare a ottimizzare il modo in cui vengono eseguite le query.Tuttavia, a volte non si ottiene la risposta giusta.Se sai che SQL Server non utilizza l'indice, osservando il piano di esecuzione nell'analizzatore di query, puoi indicare al motore di query di utilizzare un indice specifico con la seguente modifica alla query.

Select EmployeeId From Employee WITH (Index(Index_EmployeeTypeId )) Where EmployeeTypeId IN (1,2,3)

Supponendo che l'indice presente nel campo EmployeeTypeId sia denominato Index_EmployeeTypeId.

Altri suggerimenti

Di solito lo farebbe, a meno che la clausola IN non copra troppo la tabella, quindi verrà eseguita una scansione della tabella.Il modo migliore per scoprirlo nel tuo caso specifico sarebbe eseguirlo nell'analizzatore di query e controllare il piano di esecuzione.

A meno che la tecnologia non sia migliorata in modi che non riesco a immaginare negli ultimi tempi, la query "IN" mostrata produrrà un risultato che è effettivamente l'OR-ing di tre set di risultati, uno per ciascuno dei valori nell'elenco "IN".La clausola IN diventa una condizione di uguaglianza per ciascuno degli elenchi e utilizzerà un indice, se appropriato.Nel caso di ID univoci e di una tabella sufficientemente grande, mi aspetto che l'ottimizzatore utilizzi un indice.

Se tuttavia gli elementi nell'elenco dovessero essere non univoci e immagino nell'esempio che un "TypeId" sia una chiave esterna, allora sono più interessato alla distribuzione.Mi chiedo se l'ottimizzatore controllerà le statistiche per ciascun valore nell'elenco?Supponiamo che controlli il primo valore e trovi che si trova nel 20% delle righe (di una tabella abbastanza grande da essere importante).Probabilmente eseguirà la scansione della tabella.Ma per gli altri due verrà utilizzato lo stesso piano di query, anche se sono univoci?

Probabilmente è discutibile: qualcosa come una tabella Employee è probabile che sia abbastanza piccola da rimanere nella cache in memoria e probabilmente non noterai comunque una differenza tra quella e il recupero indicizzato.

E infine, mentre sto predicando, fai attenzione alla query nella clausola IN:spesso è un modo rapido per far funzionare qualcosa e (almeno per me) può essere un buon modo per esprimere il requisito, ma è quasi sempre meglio riformulare come join.Il tuo ottimizzatore potrebbe essere abbastanza intelligente da individuarlo, ma anche in questo caso potrebbe non essere così.Se al momento non controlli le prestazioni rispetto ai volumi dei dati di produzione, fallo: in questi giorni di ottimizzazione basata sui costi non puoi essere certo del piano di query finché non disponi di un carico completo e di statistiche rappresentative.Se non puoi, preparati a sorprese in produzione...

Quindi c'è il potenziale per una clausola "in" per eseguire una scansione da tavolo, ma l'ottimizzatore cercherà di elaborare il modo migliore per affrontarla?

L'utilizzo di un indice non varia tanto in base al tipo di query quanto al tipo e alla distribuzione dei dati nelle tabelle, all'aggiornamento delle statistiche della tabella e al tipo di dati effettivo della colonna .

Gli altri poster hanno ragione nel dire che verrà utilizzato un indice durante la scansione di una tabella se:

  • La query non accederà a più di una certa percentuale delle righe indicizzate (diciamo circa il 10% ma dovrebbe variare tra i DBMS).
  • In alternativa, se sono presenti molte righe, ma relativamente pochi valori univoci nella colonna, potrebbe anche essere più veloce eseguire una scansione della tabella.

L'altra variabile che potrebbe non essere così ovvia è assicurarsi che i tipi di dati dei valori confrontati siano gli stessi.In PostgreSQL, non penso che gli indici verranno utilizzati se stai filtrando su un float ma la tua colonna è composta da int.Esistono anche alcuni operatori che non supportano l'uso dell'indice (di nuovo, in PostgreSQL, l'operatore ILIKE è così).

Come notato, però, controlla sempre l'analizzatore di query in caso di dubbi e la documentazione del tuo DBMS è tua amica.

@Mike:Grazie per l'analisi dettagliata.Ci sono sicuramente alcuni punti interessanti che fai lì.L'esempio che ho pubblicato è alquanto banale, ma la base della domanda deriva dall'utilizzo di NHibernate.

Con NHibernate puoi scrivere una clausola come questa:

int[] employeeIds = new int[]{1, 5, 23463, 32523};
NHibernateSession.CreateCriteria(typeof(Employee))
.Add(Restrictions.InG("EmployeeId",employeeIds))

NHibernate genera quindi una query simile a

select * from employee where employeeid in (1, 5, 23463, 32523)

Quindi, come tu e altri avete sottolineato, sembra che ci saranno momenti in cui verrà utilizzato un indice o verrà eseguita una scansione della tabella, ma non è possibile determinarlo fino al runtime.

Select EmployeeId From Employee USE(INDEX(EmployeeTypeId))

Questa query eseguirà la ricerca utilizzando l'indice che hai creato.Per me funziona.Per favore, fai una prova..

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