2012-09-24 10 views
8

Sto eseguendo diverse query SQL di lunga durata come parte di un modulo di reporting. Queste query sono costruite dinamicamente in fase di esecuzione. A seconda dell'input dell'utente, possono essere singole o multi-statement, avere uno o più parametri e operare su una o più tabelle di database - in altre parole, la loro forma non può essere facilmente anticipata.Utilizzare SqlTransaction & IsolationLevel per operazioni di lettura lunghe?

Attualmente, sto solo l'esecuzione di queste dichiarazioni su un normale SqlConnection, cioè

using (SqlConnection cn = new SqlConnection(ConnectionString)) { 
    cn.Open(); 
    // command 1 
    // command 2 
    // ... 
    // command N 
} 

Perché queste query (davvero interrogare lotti) può prendere un po 'per l'esecuzione, io sono preoccupato per serrature sulle tavole alzando legge/scrive per altri utenti. Non è un problema se i dati di questi report cambiano durante l'esecuzione del batch; le query del report non dovrebbero mai avere la precedenza su altre operazioni su quelle tabelle, né dovrebbero bloccarle.

Per la maggior parte delle operazioni long-running/multi-statement che comportano la modifica dei dati, vorrei utilizzare le transazioni. La differenza qui è che queste query di report non stanno modificando alcun dato. Sarei corretto nel wrapping di queste query di report in un SqlTransaction al fine di controllare il loro livello di isolamento?

cioè:

using (SqlConnection cn = new SqlConnection(ConnectionString)) { 
    cn.Open(); 

    using (SqlTransaction tr = cn.BeginTransaction(IsolationLevel.ReadUncommitted)) { 
     // command 1 
     // command 2 
     // ... 
     // command N 

     tr.Commit(); 
    } 
} 

Sarebbe questo raggiungere il mio risultato desiderato? È corretto commettere una transazione, anche se nessun dato è stato modificato? C'è un altro approccio?

+0

Fare i rapporti devono essere [corretta] (http://blogs.msdn.com/b/sqlcat/archive/2007/02/01/previously-committed-rows-might- be-missed-if-nolock-hint-is-used.aspx) o no? –

+0

@RemusRusanu I report forniscono un'istantanea istantanea dei dati per un lungo periodo di tempo; non è necessario tenere conto delle modifiche che possono verificarsi a metà transazione. È probabile che le query sottostanti vengano eseguite più volte dall'utente in breve successione e l'utente non si aspetta che i risultati siano identici ogni volta. –

+1

Questo è esattamente il tipo di rapporti che vengono influenzati (male) dalle letture sporche. I conteggi e gli aggregati salteranno in modo casuale su e giù e i dati all'interno di una singola analisi (un rapporto) non saranno coerenti (ad esempio, somma debito! = Somma del credito). I tuoi utenti perdono la fiducia nel rapporto poiché sembrerà che generino dati casuali. Hai considerato ['SNAPSHOT'] (http://msdn.microsoft.com/en-us/library/ms345124 (v = sql.90) .aspx)? –

risposta

5

altro approccio potrebbe essere quello di emettere, contro la connessione:

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 

che realizza lo stesso intento, senza fare confusione con una transazione. Oppure è possibile utilizzare l'hint WITH(NOLOCK) nelle tabelle della query, che presenta il vantaggio di non modificare affatto la connessione.

È importante sottolineare che, si noti che (insolitamente): tuttavia vengono cambiate (transazione, transazioni-scope, esplicita SET, ecc), il livello di isolamento è non ripristino tra usi della stessa connessione sottostante quando lo si preleva dalla piscina. Questo significa che se il codice cambia il livello di isolamento (direttamente o indirettamente), poi nessuno del vostro codice di sa che cosa il livello di isolamento di una nuova connessione è:

using(var conn = new SqlConnection(connectionString)) { 
    conn.Open(); 
    // isolation level here could be **ANYTHING**; it could be the default 
    // if it is a brand new connection, or could be whatever the last 
    // connection was when it finished 
} 

Il che rende il WITH(NOLOCK) molto allettante.

+0

.... spiegazioni troppo brevi dei vostri suggerimenti. – ulrichb

+1

Wow, grazie per l'avvertimento sull'impostazione del livello di isolamento ... potrebbe essere pericoloso. –

+0

Impostando il livello di isolamento tramite BeginTransaction, evitiamo il problema di "livello di isolamento non ripristinato tra le connessioni" che hai menzionato? In tal caso, c'è qualche inconveniente nell'usare una transazione? – digEmAll

1

Sono d'accordo con Marc, ma in alternativa è possibile utilizzare l'hint di query NOLOCK sulle tabelle interessate. Questo ti darebbe la possibilità di controllarlo su un tavolo per livello di tabella.

Il problema con l'esecuzione di qualsiasi query senza prendere blocchi condivisi è che ti lasci aperto a risultati "non deterministici" e che le decisioni aziendali non vengano prese su questi dati.

Un approccio migliore potrebbe essere quello di esaminare i livelli di isolamento SNAPSHOT o READ_COMMITED_SNAPSHOT. Questi ti danno protezione contro le transazioni transazionali senza prendere le serrature. Il compromesso è che aumentano l'IO rispetto a TempDB. Ciascuno di questi livelli può essere applicato alla sessione come suggerito da Marc o alla tabella come suggerito.

Spero che questo aiuti