2015-10-19 11 views
13

Ultimamente sto facendo del mio meglio per cercare il modo migliore per eseguire determinate query in SQL che potrebbero potenzialmente essere eseguite in diversi modi. Tra le mie ricerche mi sono imbattuto in un bel po 'di odio per il concetto WHERE IN, a causa di una intrinseca inefficienza nel modo in cui funziona.SQL UPDATE WHERE IN (List) o UPDATE individualmente?

esempio: WHERE Col IN (val1, val2, val3)

Nel mio progetto attuale, sto facendo un aggiornamento su un grande insieme di dati e mi chiedo quale dei seguenti è più efficiente: (o se esiste una scelta migliore)

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (id1, id2, id3 ....); 

In quanto sopra, l'elenco di ID può contenere fino a 1.5k ID.

VS

Looping attraverso tutte ID di nel codice, e l'esecuzione del seguente dichiarazione per ciascuna:

UPDATE table1 SET somecolumn = 'someVal' WHERE ID = 'theID'; 

Per me, sembra più logico che il primo avrebbe funzionato meglio/più veloce, perché c'è meno query da eseguire. Detto questo, non conosco al 100% gli input e gli errori di SQL e il funzionamento dell'accodamento delle query.

Sono anche incerto su quale sarebbe più amichevole sul DB per quanto riguarda i blocchi del tavolo e altre prestazioni generali.

Informazioni generali nel caso in cui sia utile, sto utilizzando Microsoft SQL Server 2014 e il linguaggio di sviluppo primario è C#.

Qualsiasi aiuto è molto apprezzato.

EDIT:

Opzione 3:

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (SELECT ID FROM @definedTable); 

In quanto sopra, @definedTable è uno SQL 'User Defined Table Type', dove i dati all'interno passa attraverso ad una stored procedure come (in C#) tipo SqlDbType.Structured

le persone si chiedono come mai nella del ID: ID sono in un List<string> nel codice, e sono utilizzati per altre cose nel codice prima di allora di essere inviato a una stored procedure. Attualmente, gli ID stanno entrando nella stored procedure come un "Tipo di tabella definito dall'utente" con una sola colonna (ID).

Li ho pensato avendo in una tabella potrebbe essere migliore di avere il codice di concatenare una stringa massiccia e solo sputare in SP come una variabile che assomiglia id1, id2, id3, id4 ecc

+1

Hai provato a guardare il piano di esecuzione se qual è più veloce delle due query? – Japongskie

+1

Da dove provengono id1, id2, id3? Nella maggior parte dei casi pratici provengono da un altro tavolo, a seguito del filtraggio in alcune condizioni. In questo caso faresti meglio ad unirti a quel tavolo, per ottenere prestazioni decenti. –

+1

Come passate questi ID a SQL? Hai un elenco di valori nel tuo codice C# o li ottieni come risultato di un'altra query SQL? – DavidG

risposta

5

sto usando la vostra terza opzione e funziona benissimo.

La mia stored procedure ha un table-valued parameter. Vedi anche Use Table-Valued Parameters.

Nella procedura c'è una dichiarazione, nessun cicli, come hai detto tu:

UPDATE table1 SET somecolumn = 'someVal' WHERE ID IN (SELECT ID FROM @definedTable); 

E 'meglio chiamare la procedura di una volta, di 1.500 volte. È meglio avere una transazione, oltre 1.500 transazioni.

Se il numero di righe nello @definedTable supera, ad esempio, 10 K, prenderei in considerazione la scomposizione in lotti di 10 K.


tua prima variante è OK per pochi valori nella clausola IN, ma quando si arriva a molto alti numeri (60K +) si può vedere qualcosa di simile, come mostrato nella this answer:

Msg 8623, livello 16, stato 1, riga 1 Il processore di query ha esaurito le risorse interne e non è stato in grado di produrre un piano di query. Questo è un raro evento e si aspetta solo per query o query estremamente complesse che fanno riferimento a un numero molto elevato di tabelle o partizioni. Si prega di semplificare la query. Se ritieni di aver ricevuto questo messaggio per errore, contatta per ricevere ulteriori informazioni.

1

non si dovrebbe assolutamente utilizzare un ciclo e inviare un'intera nuova istruzione SQL per ogni ID. In tal caso, il motore SQL deve ricompilare l'istruzione SQL e creare un piano di esecuzione, ecc. Ogni volta.

Probabilmente la cosa migliore da fare è fare una dichiarazione preparata con un segnaposto quindi scorrere i dati eseguendo la dichiarazione per ogni valore. Quindi la dichiarazione rimane nella memoria del motore del database e rapidamente la esegue solo con il nuovo valore ogni volta che la chiami piuttosto che ricominciare da zero.

Se si dispone di un database di grandi dimensioni e/o eseguito spesso, assicurarsi anche di creare un indice su tale valore di ID, altrimenti sarà necessario eseguire una scansione completa della tabella con ogni valore.

EDIT:

Perl pseudocodice come descritto di seguito:

#!/usr/bin/perl 
use DBI; 
$dbh = DBI->connect('dbi:Oracle:MY_DB', 'scott', 'tiger', { RaiseError => 1, PrintError =>1, AutoCommit => 0 }); 
$sth = $dbh->prepare ("UPDATE table1 SET somecolumn = ? WHERE id = ?"); 
foreach $tuple (@updatetuples) { 
    $sth->execute($$tuple[1], $$tuple[0]); 
} 
$dbh->commit; 
$sth->finish; 
$dbh->disconnect; 
exit (0); 
+0

Ho pensato che i dati provenissero dal tuo programma, non un altro tavolo IT non è completamente chiaro. Ma in quel caso, penso ancora che questa sia la via migliore. Non conosco C#, quindi ecco uno pseudocodice Perl non verificato: Oh, credo di doverlo mettere in una risposta. – Laserbeak

2

La prima o la terza opzione sono la soluzione migliore. Per entrambi, si desidera un indice su table1(id).

In generale, è preferibile eseguire una query anziché più query poiché il sovraccarico dovuto al trasferimento dei dati in entrata e in uscita dal database si aggiunge. Inoltre, ogni aggiornamento avvia una transazione e la impegna - ulteriori spese generali. Detto questo, questo probabilmente non sarà importante a meno che non stiate aggiornando migliaia di record. Il sovraccarico viene misurato in centinaia di microsecondi o millisecondi, su un sistema tipico.