2009-09-05 24 views
79

Moje pytanie jest, jak uzyskać liczbę wierszy zwracanych przez kwerendy przy użyciu SqlDataReader w języku C#. Widziałem kilka odpowiedzi na ten temat, ale żadna nie była jasno zdefiniowana, z wyjątkiem tej, która stwierdza, że ​​wykonuje pętlę z metodą Read() i zwiększa licznik.Jak uzyskać liczbę wierszy przy użyciu SqlDataReader w C#

Mój problem polega na tym, że próbuję wypełnić tablicę wielowymiarową, przy czym pierwszym wierszem jest nazwa nagłówka kolumny, a każdy kolejny wiersz to dane wiersza.

Wiem, że mogę po prostu zrzucić rzeczy z listy kontrolnej i nie martwić się o to, ale dla własnego osobistego zbudowania i chciałbym również wyciągnąć dane do tablicy i jak tylko zechcę, i wyświetlić ją w różnych formatach.

Więc myślę, że nie może zrobić Read() a następnie przyrost ++ sposób, ponieważ to oznacza, że ​​będę musiał otworzyć Read() a następnie otwórz Read() ponownie, aby uzyskać ilość wierszy i następnie dane kolumn.

tylko mały przykład tego, co mówię:

int counter = 0;  

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

a następnie do pętli prowadzonym przez kolumny i pop

something.Read(); 

int dbFields = sqlRead.FieldCount; 

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

Odpowiedz

75

Są tylko dwie opcje:

  • Sprawdzaj czytając wszystkie wiersze (i wtedy równie dobrze można je przechowywać)

  • wcześniej wykonaj wyspecjalizowane zapytanie SELECT COUNT (*).

Dwukrotne przejście przez pętlę DataReadera jest naprawdę kosztowne, konieczne będzie ponowne wykonanie zapytania.

I (dzięki Pete OHanlon) druga opcja jest bezpieczna tylko dla współbieżności, gdy używana jest transakcja z poziomem izolacji migawki.

Ponieważ chcesz zachować wszystkie wiersze w pamięci, jedyną sensowną opcją jest odczytanie wszystkich wierszy w elastycznym magazynie (List<> lub DataTable), a następnie skopiowanie danych do dowolnego żądanego formatu. Operacja w pamięci zawsze będzie znacznie wydajniejsza.

+4

Henk ma rację: nie ma elementu DataReader, który pozwala uzyskać liczbę wierszy, ponieważ jest to czytnik tylko do przodu. Lepiej jest najpierw zdobyć liczenie, a następnie wykonać zapytanie, być może w zapytaniu wielowarstwowym, aby tylko raz trafić w bazę danych. – flipdoubt

+12

Problem z wyspecjalizowanym licznikiem polega na tym, że istnieje możliwość, że liczba będzie różna od liczby zwróconych wierszy, ponieważ ktoś inny zmienił dane w sposób, który prowadzi do liczby zwróconych wierszy. –

+0

Pete, masz rację, będzie to wymagało kosztownej izolacji IsolationLevel. –

4

Nie można uzyskać liczby wierszy bezpośrednio z czytnika danych, ponieważ jest to tzw. Kursor typu firehose - co oznacza, że ​​dane są odczytywane wiersz po wierszu na podstawie wykonywanego odczytu. Odradzam robienie 2 odczytów danych, ponieważ istnieje potencjał, że dane zmieniły się pomiędzy robieniem 2 czytań, a zatem otrzymasz inne wyniki.

Co można zrobić, to odczytać dane do tymczasowej struktury i użyć ich zamiast drugiego odczytu. Ewentualnie musisz zmienić mechanizm pozyskiwania danych i zamiast tego użyć czegoś takiego jak DataTable.

6

Powyżej zbiór danych lub wpisany zestaw danych może być dobrą, skróconą strukturą, której można użyć do filtrowania. SqlDataReader ma za zadanie bardzo szybko odczytać dane. Gdy jesteś w pętli while(), nadal jesteś połączony z DB i czeka na to, co zrobisz, aby odczytać/przetworzyć następny wynik zanim przejdzie dalej.W takim przypadku możesz uzyskać lepszą wydajność, jeśli pobierzesz wszystkie dane, zamkniesz połączenie z bazą danych i przetworzy wyniki "offline".

Wydaje się, że ludzie nienawidzą zestawów danych, więc powyższe może być wykonane z kolekcji silnie wpisanych obiektów, jak również.

+1

Sam uwielbiam DataSets, ponieważ są dobrze napisane i niezwykle użyteczne ogólne przedstawienie danych opartych na tabelach. Co dziwne, zauważyłem, że większość ludzi, którzy unikają DataSet dla ORM to ci sami ludzie, którzy starają się napisać własny kod, aby był jak najbardziej ogólny (zazwyczaj bezsensowny). – MusiGenesis

+4

Daniel, "powyżej" nie jest dobrym sposobem na odniesienie się do innej odpowiedzi. –

17

Klasy relacyjne/DataSet jest odpowiednią opcją.

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

Ładowanie wszystkich danych tylko po to, aby uzyskać liczbę wierszy, nie jest dobrym pomysłem. Zwłaszcza z DataTable, co spowoduje obciążenie pamięci błędów. – shatl

+0

@shatl - Zgadzam się! ale ta opcja jest sugerowana przez wiele plakatów, w tym Henk Holterman i myślę, że OP chciał się upewnić, że nie ma żadnej bezpośredniej metody, która zwraca liczbę wierszy. – adatapost

+1

@AVD DataTable jest waga tylko po to, aby uzyskać liczbę wierszy. –

5

Jeśli nie trzeba pobrać cały wiersz i chcą uniknąć zrobić podwójne zapytanie, prawdopodobnie można spróbować coś takiego:

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(); 
     } 

Być może trzeba będzie dodać transakcji nie pewien, czy ponowne użycie tego samego polecenia automatycznie doda na nim transakcję ...

+0

Ktoś może powiedzieć, że @@ ROWCOUNT zawsze polega na ostatnim zapytaniu, które działa powyżej? Problemy, jeśli wiele połączeń uruchamia zapytania równoległe? – YvesR

+0

Nie powinno to stanowić problemu, jeśli używasz transakcji ... –

+0

Czy konieczne jest wykonanie 'sqlCon.Zamknij(); '? Myślałem, że "używanie" powinno zrobić to za Ciebie. – bluish

0

Mam także do czynienia z sytuacją, gdy potrzebowałem zwrócić najwyższy wynik, ale chciałem również uzyskać sumę wierszy zgodną z zapytaniem. I finalnie dostaję się do tego rozwiązania:

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; 
    } 
Powiązane problemy