2009-09-05 63 views
79

Mi pregunta es cómo obtener el número de filas devuelto por una consulta utilizando SqlDataReader en C#. He visto algunas respuestas al respecto, pero ninguna estaba claramente definida, excepto una que indica que se debe hacer un ciclo while con el método Read() e incrementar un contador.Cómo obtener el número de filas con SqlDataReader en C#

Mi problema es que estoy tratando de llenar una matriz multidimensional con la primera fila siendo los nombres de los encabezados de las columnas y cada fila después de los datos de las filas.

Sé que puedo simplemente volcar las cosas en un control de lista y no preocuparme por ello, pero para mi propia edificación personal y también me gustaría extraer los datos dentro y fuera de la matriz como lo elija y lo muestre en diferentes formatos.

así que creo que no puedo hacer el Read() y luego incrementar manera ++ porque eso significa que tendría que abrir Read()Read() y luego abierta de nuevo para obtener la cantidad de filas y luego datos de columna.

Sólo un pequeño ejemplo de lo que estoy hablando:

int counter = 0;  

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

y luego un bucle para ejecutar a través de las columnas y el pop

something.Read(); 

int dbFields = sqlRead.FieldCount; 

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

Respuesta

75

Sólo hay dos opciones:

  • Entérate leyendo todas las filas (y luego también puedes guardarlas)

  • ejecutar de antemano una consulta especializada SELECT COUNT (*).

Pasar dos veces por el bucle de DataReader es realmente caro, tendría que volver a ejecutar la consulta.

Y (gracias a Pete OHanlon) la segunda opción solo es compatible con concurrencia cuando utiliza una transacción con un nivel de aislamiento de instantáneas.

Como quiera terminar almacenando todas las filas en la memoria, la única opción sensata es leer todas las filas en un almacenamiento flexible (List<> o DataTable) y luego copiar los datos al formato que desee. La operación en memoria siempre será mucho más eficiente.

+4

Henk tiene razón: no hay ningún miembro del DataReader que le permita obtener el número de filas porque es un lector de solo avance. Es mejor primero obtener el recuento y luego ejecutar la consulta, tal vez en una consulta de resultados múltiples para que solo llegue a la base de datos una vez. – flipdoubt

+12

El problema con el recuento especializado es que existe la posibilidad de que el recuento sea diferente del número de filas devueltas porque alguien más ha cambiado los datos de una manera que conduce a la cantidad de filas que se devuelven. –

+0

Pete, tienes razón, requeriría un IsolationLevel caro. –

4

No se puede obtener un recuento de filas directamente de un lector de datos porque es lo que se conoce como un cursor firehose, lo que significa que los datos se leen fila por fila en función de la lectura que se realiza. Aconsejo no hacer 2 lecturas sobre los datos porque existe la posibilidad de que los datos hayan cambiado entre hacer las 2 lecturas, y así obtendría resultados diferentes.

Lo que podría hacer es leer los datos en una estructura temporal, y usarlos en lugar de la segunda lectura. Alternativamente, tendrá que cambiar el mecanismo por el cual recupera los datos y usar algo así como una DataTable en su lugar.

6

Según lo anterior, un conjunto de datos o un conjunto de datos tipeados podría ser una buena estructura de temorary que podría usar para filtrar. Un SqlDataReader está destinado a leer los datos muy rápidamente. Mientras está en el ciclo while() todavía está conectado a la base de datos y está esperando que haga lo que sea que esté haciendo para leer/procesar el siguiente resultado antes de continuar.En este caso, puede obtener un mejor rendimiento si obtiene todos los datos, cierra la conexión al DB y procesa los resultados "fuera de línea".

Las personas parecen odiar los conjuntos de datos, por lo que lo anterior también se puede hacer con una colección de objetos fuertemente tipados.

+1

Me encantan los DataSets, ya que son una representación genérica bien escrita y extremadamente útil de datos basados ​​en tablas. Curiosamente, me he dado cuenta de que la mayoría de las personas que evitan el DataSet para ORM son las mismas personas que intentan escribir su propio código para ser lo más genéricos posible (por lo general, sin sentido). – MusiGenesis

+4

Daniel, 'arriba' no es una buena manera de hacer referencia a otra respuesta. –

17

Las clases relacionales/DataSet es la opción adecuada.

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

Cargando todos los datos solo para obtener el número de filas no es una buena idea. Especialmente con DataTable, que dará como resultado una sobrecarga de memoria de errores. – shatl

+0

@shatl - ¡De acuerdo! pero esta opción es sugerida por muchos carteles, incluido Henk Holterman, y creo que OP quería asegurarse de que no haya ningún método directo que devuelva el recuento de filas. – adatapost

+1

@AVD DataTable es muy pesado solo para obtener el recuento de filas. –

5

Si no es necesario para recuperar toda la fila y desea evitar hacer una consulta doble, es probable que pueda intentar algo así como que:

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

puede que tenga que añadir una transacción no seguro si reutilizando el mismo comando agregará automáticamente una transacción en él ...

+0

¿Alguien puede decir si @@ ROWCOUNT siempre depende de la última consulta que se ejecuta arriba? ¿Problemas si muchas conexiones ejecutan consultas paralelas? – YvesR

+0

No debería ser un problema si usa transacciones ... –

+0

¿Es necesario hacer 'sqlCon.Close(); '? Pensé que 'using' debería hacerlo por ti. – bluish

0

También me enfrento a una situación en la que necesitaba devolver un resultado superior pero también quería obtener el total de filas que coincidían con la consulta. finalmente llego a esta solución:

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; 
    } 
Cuestiones relacionadas