Devo scrivere una query per recuperare una grande lista di id.
Supportiamo molti backend (MySQL, Firebird, SQLServer, Oracle, PostgreSQL …) quindi ho bisogno di scrivere un SQL standard.
La dimensione del set ID potrebbe essere grande, la query verrebbe generata a livello di codice. Quindi, qual è l’approccio migliore?
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
La mia domanda qui è. Cosa succede se n è molto grande? Inoltre, per quanto riguarda le prestazioni?
SELECT * FROM TABLE WHERE ID = id1 OR ID = id2 OR ... OR ID = idn
Penso che questo approccio non abbia un limite, ma per quanto riguarda le prestazioni se n è molto grande?
foreach (id in myIdList) { item = GetItemByQuery("SELECT * FROM TABLE WHERE ID = " + id); myObjectList.Add(item); }
Abbiamo riscontrato alcuni problemi con questo approccio quando il server del database viene interrogato sulla rete. Normalmente è meglio fare una query che recuperi tutti i risultati, meglio di molte piccole query. Forse sto sbagliando.
Quale sarebbe una soluzione corretta per questo problema?
L’opzione 1 è l’unica buona soluzione.
L’opzione 2 fa lo stesso, ma ripetete il nome della colonna molte volte; Inoltre, il motore SQL non riconosce immediatamente che si desidera controllare se il valore è uno dei valori in una lista fissa. Tuttavia, un buon motore SQL potrebbe ottimizzarlo per avere prestazioni uguali come con IN
. Rimane comunque il problema di leggibilità …
L’opzione 3 è semplicemente orribile in termini di prestazioni. Invia una query ad ogni ciclo e martella il database con piccole query. Impedisce inoltre di utilizzare eventuali ottimizzazioni per “il valore è uno di quelli in una data lista”
Un approccio alternativo potrebbe essere quello di utilizzare un’altra tabella per contenere valori di identificazione. Questa altra tabella può quindi essere unita internamente su TABLE per vincolare le righe restituite. Ciò avrà il grande vantaggio che non avrai bisogno di SQL dinamico (problematico nel migliore dei casi) e non avrai una clausola IN infinitamente lunga.
Dovresti troncare quest’altra tabella, inserire un numero elevato di righe, quindi creare un indice per facilitare il rendimento del join. Ti consente anche di staccare l’accumulo di queste righe dal recupero dei dati, offrendoti forse più opzioni per ottimizzare le prestazioni.
Aggiornamento : Sebbene tu possa usare una tabella temporanea, non intendevo implicare che tu debba o anche dovresti. Una tabella permanente utilizzata per i dati temporanei è una soluzione comune con pregi oltre a quella descritta qui.
Quello che suggeriva Ed Guiness è davvero un richiamo alle prestazioni, ho avuto una domanda come questa
select * from table where id in (id1,id2.........long list)
cosa ho fatto :
DECLARE @temp table( ID int ) insert into @temp select * from dbo.fnSplitter('#idlist#')
Quindi interno unito alla temp con la tabella principale:
select * from table inner join temp on temp.id = table.id
E le prestazioni sono migliorate drasticamente.
La prima opzione è sicuramente l’opzione migliore.
SELECT * FROM TABLE WHERE ID IN (id1, id2, ..., idn)
Tuttavia considerando che la lista di id è molto grande , ad esempio milioni, dovresti considerare le dimensioni del chunk come di seguito:
Perché dovresti dividerti in pezzi?
Non riceverai mai un’eccezione di overflow di memoria che è molto comune in scenari come il tuo. Otterrai un numero ottimizzato di chiamate al database con prestazioni migliori.
Ha sempre funzionato come un fascino per me. Spero che possa funzionare anche per i miei colleghi sviluppatori 🙂
Nella maggior parte dei sistemi di database, IN (val1, val2, …)
e una serie di OR
sono ottimizzati per lo stesso piano.
Il terzo modo sarebbe importare la lista di valori in una tabella temporanea e unirla, che è più efficiente nella maggior parte dei sistemi, se ci sono molti valori.
Puoi leggere questo articolo:
Il campione 3 sarebbe il peggiore risultato di tutti perché si sta colpendo il database innumerevoli volte senza un motivo apparente.
Caricare i dati in una tabella temporanea e poi unirsi a quelli sarebbe di gran lunga il più veloce. Successivamente, l’IN dovrebbe funzionare leggermente più velocemente rispetto al gruppo di OR.
Penso che tu intenda SqlServer ma su Oracle hai un limite difficile quanti elementi IN puoi specificare: 1000.
Facendo SELECT * da MyTable dove il comando id in () su una tabella SQL di Azure con 500 milioni di record ha comportato un tempo di attesa di> 7min!
In questo caso, invece, restituiva immediatamente i risultati:
select b.id, a.* from MyTable a join (values (250000), (2500001), (2600000)) as b(id) ON a.id = b.id
Usa un join.