2010-04-23 14 views
5

Ho bisogno di aggiornare circa 250k righe su una tabella e ogni campo da aggiornare avrà un valore diverso a seconda della riga stessa (non calcolato in base all'id della riga o della chiave ma esternamente) .Modo più rapido per aggiornare 250k righe con SQL

Ho provato con una query parametrizzata ma risulta essere lenta (posso ancora provare con un parametro valore tabella, SqlDbType.Structured, in SQL Server 2008, ma mi piacerebbe avere un modo generale di fare su diversi database tra cui MySql, Oracle e Firebird).

facendo un enorme concat di aggiornamenti individuali, è anche lento (ma circa 2 volte più veloce di fare migliaia di chiamate individuali (andata e ritorno!) Utilizzando query parametrizzate)

Che sulla creazione di una tabella temporanea e l'esecuzione di un aggiornamento unendo la mia tavolo e il tmp? Funzionerà più velocemente?

+0

si può dare un esempio di algoritmo che si utilizza per determinare come i dati con il cambiamento? Cerca tavoli? – Glennular

+0

I dati da modificare provengono dall'esterno del db, non c'è modo di aggiornare usando altre tabelle – pablo

+0

"Che ne dici di creare una tabella temporanea e di eseguire un aggiornamento che unisce la mia tabella e il tmp? Funzionerà più velocemente?" <- Penso che questo sia il modo migliore per ottenere prestazioni. se sono in un database diverso, prova a creare prima una tabella di staging, quindi aggiorna il join da quella tabella. – Hao

risposta

7

Quanto lento è "lento"?

Il problema principale di questo è che si creerebbe un enorme ingresso nel file di log del database (nel caso ci sia una caduta di tensione a metà strada attraverso l'aggiornamento, il database deve registrare ogni azione in modo che possa far ritirare in caso di fallimento). Questo è molto probabilmente da dove viene la "lentezza", più di ogni altra cosa (anche se ovviamente con un numero così elevato di righe, ci sono altri modi per rendere la cosa inefficiente [ad esempio, fare un viaggio di andata e ritorno DB per aggiornamento sarebbe insopportabilmente lento] , Sto solo dicendo che una volta eliminate le cose ovvie, troverai comunque che è piuttosto lento).

Ci sono alcuni modi in cui è possibile farlo in modo più efficiente. Uno sarebbe quello di fare l'aggiornamento in blocchi, 1.000 righe alla volta, ad esempio. In questo modo, il database scrive molte piccole voci di registro, piuttosto che una molto grande.

Un altro modo sarebbe quello di spegnere - o girare "in basso" - la registrazione del database per la durata dell'aggiornamento. In SQL Server, ad esempio, è possibile impostare il Recovery Model al "semplice" o "aggiornamento di massa", che accelerarlo considerevolmente (con l'avvertenza che si è più a rischio se c'è una mancanza di corrente o qualcosa durante l'aggiornamento).

Modifica Giusto per espandere un po 'di più, probabilmente il modo più efficace per eseguire effettivamente le query in primo luogo sarebbe quello di fare un BULK INSERT di tutte le nuove righe in una tabella temporanea, e poi fare un unico UPDATE della tabella esistente da quella (o di fare il UPDATE in blocchi di 1.000 come ho detto sopra). La maggior parte della mia risposta era affrontare il problema una volta che hai implementato in quel modo: potete ancora trovare è piuttosto lento ...

+0

Sì, quando si dice "db roundtrip per update" si intende probabilmente la query "parametrizzata", giusto? – pablo

+0

Sì, intendo dire "UPDATE blah SET blah" 250.000 volte. Anche con query parametrizzate, sarà * molto * lento perché stai ancora facendo un giro di andata e ritorno nel database per ognuno di essi. –

+0

Sì, facendo solo uno: "Aggiornamento blah set blah; aggiornamento blah1 set blah1; ...." è molto più veloce rispetto all'utilizzo di query parametrizzate, ma è più lento (intendo, più lento dell'inserimento della stessa quantità di righe utilizzando un BulkCopy in SQL Server) – pablo

2

Se le colonne aggiornati sono parte di indici si potrebbe

  • goccia questi indici
  • fanno l'aggiornamento
  • ricreare gli indici.

Se hai bisogno di questi indici per recuperare i dati, beh, non aiuta.

+0

Sì, ma gli indici non sembrano essere il problema, ma i roundtrip. – pablo

+0

Anche l'eliminazione e la ricreazione degli indici saranno utili, sì. Ma se stai facendo singoli UPDATE, allora il colpo di performance di aggiornare gli indici ogni volta è probabilmente "affogato" dai round-trips della rete e così via. –

+1

Quindi hai la tua risposta allora. L'unico modo per sbarazzarsi del viaggio di andata è fare il calcolo sul lato server. Inserire la logica in una stored procedure e creare un singolo aggiornamento. –

3

chiamata di una stored procedure, se possibile

1

È consigliabile utilizzare lo SqlBulkCopy con il set di KeepIdentities bandiera.

Come parte di un SqlTransaction, eseguire una query per SELEZIONARE tutti i record che devono essere aggiornati e quindi ELIMINALI, restituendo i record selezionati (e ora rimossi). Leggeteli in C# in un singolo batch. Aggiorna i record sul lato C# in memoria, ora che hai ristretto la selezione e poi SqlBulkCopy quelli precedenti, le chiavi e tutto il resto. E non dimenticare di impegnare la transazione. È più lavoro, ma è molto veloce.

0

Ecco cosa farei:

  1. Recuperare l'intera tabella, vale a dire, le colonne è necessario al fine di calcolare/recuperare/trovare/produrre i cambiamenti esternamente
  2. Calcolare/produrre quei cambiamenti
  3. Eseguire un inserto di massa in una tabella temporanea, caricando le informazioni necessarie sul lato server per eseguire le modifiche. Ciò richiederebbe le informazioni chiave + nuovi valori per tutte le righe che intendi modificare.
  4. Eseguire SQL sul server per copiare i nuovi valori dalla tabella temporanea nella tabella di produzione.

Pro:

  • in esecuzione il server-side ultimo passo è più veloce di esecuzione tonnellate e tonnellate di SQL individuale, quindi si sta andando a bloccare la tabella in questione per un tempo più breve
  • inserimento di massa di questo tipo è veloce

Contro:

  • richiede più spazio nel database per la tabella temporanea
  • produce più dati di log, la registrazione sia l'inserimento di massa e le modifiche alla tabella di produzione
0

Qui ci sono cose che possono rendere gli aggiornamenti lento:

  • aggiornamenti esecuzione uno per uno tramite query parametrizzata
    • soluzione: fare l'aggiornamento in un unico prospetto
  • transazione di grandi dimensioni crea grande voce di registro
  • indici di aggiornamento (RDBMS aggiorneranno indice dopo ogni riga.Se si modifica colonna indicizzata, potrebbe essere molto costosa su grande tavolo)
    • se è possibile, goccia indici prima dell'aggiornamento e ricreare dopo
  • campo aggiornamento che ha vincolo di chiave esterna - per ogni record inserito RDBMS andrà a cercare adeguata chiave
    • se è possibile, disattivare vincoli di chiave esterna prima di aggiornamento e di consentire, dopo l'aggiornamento
  • trigger e fila controlli di livello
    • se è possibile, disattivare i trigger prima dell'aggiornamento e li Abilitazione dopo
Problemi correlati