leer - Cómo obtener el número de filas usando SqlDataReader en C#
sqldatareader to datatable (6)
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 se definió claramente, excepto una que indica hacer un ciclo while con el método Read()
e incrementar un contador.
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 posterior a los datos de las filas.
Sé que puedo simplemente volcar las cosas en un control de lista y no preocuparme por eso, sino por mi propia edificación personal y también me gustaría extraer los datos dentro y fuera de la matriz como lo elija y mostrar en diferentes formatos.
Así que creo que no puedo Read()
y luego incrementar ++ porque eso significa que tendría que abrir Read()
y luego abrir Read()
nuevamente para obtener la cantidad de filas y luego los datos de la columna.
Solo un pequeño ejemplo de lo que estoy hablando:
int counter = 0;
while (sqlRead.Read())
{
//get rows
counter++
}
y luego un ciclo para ejecutar las columnas y pop
something.Read();
int dbFields = sqlRead.FieldCount;
for (int i = 0; i < dbFields; i++)
{
// do stuff to array
}
No se puede obtener un recuento de las 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.
Por encima, un conjunto de datos o un conjunto de datos tipeados podría ser una buena estructura temoraria que podría utilizar 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".
La gente parece odiar los conjuntos de datos, por lo que lo anterior también podría hacerse con una colección de objetos fuertemente tipados.
Si no necesita recuperar toda la fila y desea evitar realizar una doble consulta, probablemente pueda intentar algo como eso:
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();
}
Es posible que deba agregar una transacción, pero no está seguro de si reutilizar el mismo comando agregará automáticamente una transacción en él ...
Solo hay dos opciones:
Averigüe leyendo todas las filas (y luego también puede almacenarlas)
ejecutar una consulta especializada SELECT COUNT (*) de antemano.
Pasar dos veces por el bucle de DataReader es realmente costoso, tendría que volver a ejecutar la consulta.
Y (gracias a Pete OHanlon) la segunda opción solo es compatible con concurrencia cuando usa una transacción con un nivel de aislamiento de instantánea.
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.
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 donde coincidía 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;
}
Las clases relacionales / DataSet es la opción adecuada.
using (DataTable dt = new DataTable())
{
dt.Load(sqlRead);
Console.WriteLine(dt.Rows.Count);
}