2009-09-05 17 views
79

La mia domanda è come ottenere il numero di righe restituite da una query utilizzando SqlDataReader in C#. Ho visto alcune risposte a riguardo ma nessuna è stata definita chiaramente tranne una che afferma di fare un ciclo while con il metodo Read() e incrementare un contatore.Come ottenere il numero di righe utilizzando SqlDataReader in C#

Il mio problema è che sto cercando di riempire un array multidimensionale con la prima riga come i nomi delle intestazioni di colonna e ogni riga dopo quella per essere i dati di riga.

So che posso semplicemente scaricare le cose in un controllo List e non preoccuparmi di questo, ma per la mia personale edificazione e vorrei anche estrarre i dati dentro e fuori dall'array mentre lo scelgo e lo visualizzo in diversi formati.

Quindi penso che non posso fare il Read() e quindi incrementare modo ++, perché questo significa che avrei dovuto aprire Read() e quindi aprire Read() di nuovo per ottenere quantità di righe e poi dati della colonna.

Solo un piccolo esempio di cosa sto parlando:

int counter = 0;  

while (sqlRead.Read()) 
{ 
    //get rows 
    counter++ 
} 

e poi un ciclo for per eseguire attraverso le colonne e pop

something.Read(); 

int dbFields = sqlRead.FieldCount; 

for (int i = 0; i < dbFields; i++) 
{ 
    // do stuff to array 
} 

risposta

75

ci sono solo due opzioni:

  • Scoprilo leggendo tutte le righe (e quindi potresti anche memorizzarle)

  • eseguire preventivamente una richiesta SELECT COUNT (*) specializzata.

Passare due volte attraverso il ciclo DataReader è molto costoso, è necessario eseguire nuovamente la query.

E (grazie a Pete OHanlon) la seconda opzione è protetta dalla concorrenza solo quando si utilizza una transazione con un livello di isolamento Snapshot.

Poiché si desidera archiviare tutte le righe nella memoria, l'unica opzione ragionevole è quella di leggere tutte le righe in una memoria flessibile (List<> o DataTable) e quindi copiare i dati in qualsiasi formato desiderato. L'operazione in memoria sarà sempre molto più efficiente.

+4

Henk ha ragione: non esiste un membro di DataReader che ti permetta di ottenere il numero di righe perché è un lettore di sola andata. È meglio fare prima ottenere il conteggio e poi eseguire la query, magari in una query a più risultati in modo da colpire il database una sola volta. – flipdoubt

+12

Il problema con il conteggio specializzato è che esiste il potenziale per il conteggio di essere diverso dal numero di righe restituite perché qualcun altro ha modificato i dati in un modo che porta al numero di righe restituite. –

+0

Pete, hai ragione, richiederebbe un costoso IsolationLevel. –

4

Non è possibile ottenere il conteggio delle righe direttamente da un lettore di dati perché è noto come un cursore firehose. Ciò significa che i dati vengono letti riga per riga in base alla lettura eseguita. Mi sconsiglia di fare 2 letture sui dati perché c'è il potenziale che i dati sono cambiati tra fare le 2 letture, e quindi otterresti risultati diversi.

Quello che potresti fare è leggere i dati in una struttura temporanea e usarli al posto della seconda lettura. In alternativa, è necessario modificare il meccanismo con il quale recuperare i dati e utilizzare invece qualcosa come un DataTable.

6

Per quanto sopra, un set di dati o un set di dati tipizzato potrebbe essere una buona struttura tematica che è possibile utilizzare per eseguire il filtraggio. Un SqlDataReader è pensato per leggere i dati molto rapidamente. Mentre sei nel ciclo while() sei ancora connesso al DB ed è in attesa che tu faccia qualsiasi cosa tu stia facendo per leggere/elaborare il risultato successivo prima che si muova.In questo caso, è possibile ottenere prestazioni migliori se si inseriscono tutti i dati, si chiude la connessione al DB e si elaborano i risultati "offline".

Le persone sembrano odiare i set di dati, quindi quanto sopra potrebbe essere fatto anche con una raccolta di oggetti fortemente tipizzati.

+1

Amo DataSet personalmente, poiché sono una rappresentazione generica ben scritta ed estremamente utile di dati basati su tabelle. Stranamente, ho notato che la maggior parte delle persone che evitano il DataSet per ORM sono le stesse persone che cercano di scrivere il proprio codice per essere il più generico possibile (di solito inutilmente). – MusiGenesis

+4

Daniel, "sopra" non è un buon modo per fare riferimento a un'altra risposta. –

17

Classi relazionali/DataSet è l'opzione adatta.

using (DataTable dt = new DataTable()) 
{ 
    dt.Load(sqlRead); 
    Console.WriteLine(dt.Rows.Count); 
} 
+6

Il caricamento di tutti i dati solo per ottenere il numero di righe non è una buona idea. Soprattutto con DataTable, che si tradurrà in sovraccarico di memoria bug. – shatl

+0

@shatl - Accetto! ma questa opzione è suggerita da molti poster tra cui Henk Holterman e penso che l'OP volesse assicurarsi che non ci sia alcun metodo diretto che restituisca il conteggio delle righe. – adatapost

+1

@AVD DataTable è pesante solo per ottenere il conteggio delle righe. –

5

Se non c'è bisogno di recuperare tutte le fila e vuole evitare di fare una doppia domanda, probabilmente si può provare qualcosa di simile:

using (var sqlCon = new SqlConnection("Server=127.0.0.1;Database=MyDb;User Id=Me;Password=glop;")) 
     { 
     sqlCon.Open(); 

     var com = sqlCon.CreateCommand(); 
     com.CommandText = "select * from BigTable"; 
     using (var reader = com.ExecuteReader()) 
     { 
      //here you retrieve what you need 
     } 

     com.CommandText = "select @@ROWCOUNT"; 
     var totalRow = com.ExecuteScalar(); 

     sqlCon.Close(); 
     } 

potrebbe essere necessario aggiungere una transazione non certo se riutilizzare lo stesso comando aggiungerà automaticamente una transazione su di esso ...

+0

Chiunque può dire se @@ ROWCOUNT fa sempre affidamento sull'ultima query eseguita sopra? Problemi se molte connessioni eseguono query in parallelo? – YvesR

+0

Non dovrebbe essere un problema se si utilizzano le transazioni ... –

+0

È necessario eseguire 'sqlCon.Close(); '? Pensavo che "usare" dovesse farlo per te. – bluish

0

Mi trovo inoltre ad affrontare una situazione in cui dovevo restituire un risultato massimo, ma volevo anche ottenere le righe totali che corrispondevano alla query. Finalmente arriva a questa soluzione:

public string Format(SelectQuery selectQuery) 
    { 
     string result; 

     if (string.IsNullOrWhiteSpace(selectQuery.WherePart)) 
     { 
     result = string.Format(
@" 
declare @maxResult int; 
set @maxResult = {0}; 

WITH Total AS 
(
SELECT count(*) as [Count] FROM {2} 
) 
SELECT top (@maxResult) Total.[Count], {1} FROM Total, {2}", m_limit.To, selectQuery.SelectPart, selectQuery.FromPart); 
     } 
     else 
     { 
     result = string.Format(
@" 
declare @maxResult int; 
set @maxResult = {0}; 

WITH Total AS 
(
SELECT count(*) as [Count] FROM {2} WHERE {3} 
) 
SELECT top (@maxResult) Total.[Count], {1} FROM Total, {2} WHERE {3}", m_limit.To, selectQuery.SelectPart, selectQuery.FromPart, selectQuery.WherePart); 
     } 

     if (!string.IsNullOrWhiteSpace(selectQuery.OrderPart)) 
     result = string.Format("{0} ORDER BY {1}", result, selectQuery.OrderPart); 

     return result; 
    } 
Problemi correlati