Ma question est de savoir comment obtenir le nombre de lignes renvoyées par une requête en utilisant SqlDataReader
en C #. J'ai vu quelques réponses à ce sujet, mais aucune n'a été clairement définie, à l'exception de l'une d'elles qui dit de faire une boucle while avec la méthode Read()
et d'incrémenter un compteur.
Mon problème est que j'essaye de remplir un tableau multi-dimensionnel avec la première ligne étant les noms d'en-tête de colonne et chaque ligne suivante, les données de la ligne.
Je sais que je peux simplement déposer les éléments dans un contrôle List sans m'inquiéter, mais pour ma propre édification personnelle, j'aimerais également extraire les données de la matrice à ma guise et les afficher dans différents formats.
Je pense donc que je ne peux pas utiliser la méthode Read()
, puis incrémenter ++, car cela signifie que je devrais ouvrir Read()
, puis rouvrir Read()
pour obtenir le nombre de lignes et les données de colonne.
Juste un petit exemple de ce dont je parle:
int counter = 0;
while (sqlRead.Read())
{
//get rows
counter++
}
puis une boucle for pour parcourir les colonnes et pop
something.Read();
int dbFields = sqlRead.FieldCount;
for (int i = 0; i < dbFields; i++)
{
// do stuff to array
}
Il n'y a que deux options:
Découvrez-le en lisant toutes les lignes (et vous pourriez aussi bien les stocker)
exécuter préalablement une requête spécialisée SELECT COUNT (*).
Passer deux fois à travers la boucle DataReader est très coûteux, il vous faudrait ré-exécuter la requête.
Et (grâce à Pete OHanlon), la deuxième option n'est sécurisée contre la concurrence que lorsque vous utilisez une transaction avec un niveau d'isolation Snapshot.
Puisque vous souhaitez quand même stocker toutes les lignes en mémoire, la seule option judicieuse consiste à lire toutes les lignes dans un stockage flexible (List<>
ou DataTable
), puis à copier les données dans le format de votre choix. L'opération en mémoire sera toujours beaucoup plus efficace.
Si vous n'avez pas besoin de récupérer toute la ligne et que vous voulez éviter de faire une double requête, vous pouvez probablement essayer quelque chose comme ça:
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();
}
Vous devrez peut-être ajouter une transaction sans savoir si la réutilisation de la même commande ajoutera automatiquement une transaction dessus ...
Comme indiqué ci-dessus, un jeu de données ou un jeu de données typé peut constituer une bonne structure temorary que vous pouvez utiliser pour filtrer. Un SqlDataReader est conçu pour lire les données très rapidement. Pendant que vous êtes dans la boucle while (), vous êtes toujours connecté à la base de données et celle-ci attend que vous fassiez ce que vous faites pour lire/traiter le résultat suivant avant de continuer. Dans ce cas, vous obtiendrez de meilleures performances si vous extrayez toutes les données, fermez la connexion à la base de données et traitez les résultats "hors ligne".
Les gens semblent détester les ensembles de données, ce qui pourrait être fait avec une collection d'objets fortement typés.
Vous ne pouvez pas obtenir un nombre de lignes directement à partir d'un lecteur de données, car il s'agit de ce que l'on appelle un curseur Firehose, ce qui signifie que les données sont lues ligne par ligne en fonction de la lecture en cours. Je vous déconseille de faire 2 lectures sur les données car il est possible que les données aient changé entre les deux lectures et vous obtiendriez donc des résultats différents.
Ce que vous pouvez faire, c'est lire les données dans une structure temporaire et les utiliser à la place de la deuxième lecture. Sinon, vous devrez modifier le mécanisme de récupération des données et utiliser à la place quelque chose comme un DataTable.
Je suis également confronté à une situation dans laquelle je devais renvoyer un résultat supérieur, mais je souhaitais également obtenir le nombre total de lignes correspondant à la requête. Je parviens enfin à cette solution:
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;
}
pour compléter la réponse de Pit et pour de meilleures performances:.
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;select @@ROWCOUNT;";
using (var reader = com.ExecuteReader())
{
while(reader.read()){
//iterate code
}
int totalRow = 0 ;
reader.NextResult(); //
if(reader.read()){
totalRow = (int)reader[0];
}
}
sqlCon.Close();
}