IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C# Discussion :

Requete SQL vs boucle For


Sujet :

C#

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    166
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Octobre 2010
    Messages : 166
    Par défaut Requete SQL vs boucle For
    Bonsoir

    je voudrais avoir votre avis.

    je suis en train de faire une application où j'utilise une base SQLce, j'affiche mes requêtes dans un datagridview.

    mais question est la suivante :

    sachant que ma base pourra avoir plusieurs milliers lignes (car suivi de production), que dans une colonne j'ai un code défaut (pièce conforme si code défaut = 0, et mauvaise si != 0) et que je souhaite compter le nombre de pièces bonnes et le nombre de pièces mauvaises.

    quelle sera la commande la plus rapide ? moins "gourmande" ?

    faire une boucle FOR et un test du contenu de chaque cellule de la colonne de défauts pour incrémenter mes compteurs ?
    ou bien faire 2 requêtes du style "SELECT * from maTable WHERE defaut = '0'" et "SELECT * from maTable WHERE defaut <> '0'" ?

    merci

    @++

    KaloOopS

  2. #2
    Membre habitué
    Femme Profil pro
    Étudiant
    Inscrit en
    Décembre 2010
    Messages
    9
    Détails du profil
    Informations personnelles :
    Sexe : Femme
    Âge : 35
    Localisation : Suisse

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2010
    Messages : 9
    Par défaut
    Bonsoir,

    Je suis pas une experte mais personnellement je ferais une requête de type
    SELECT COUNT(*) WHERE ...
    La fonction COUNT() se charge de compter le nombre d'enregistrements.
    Comme ça tout se fait directement dans ta base de données, probablement moins lourd qu'une incrémentation dans le programme.

  3. #3
    Membre chevronné
    Homme Profil pro
    Développeur .NET
    Inscrit en
    Août 2008
    Messages
    381
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Développeur .NET

    Informations forums :
    Inscription : Août 2008
    Messages : 381
    Par défaut
    Moi aussi je pencherais fortement pour la requête.

  4. #4
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 259
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 259
    Par défaut
    Le test mérite d'être fait et donnera gagnantes de très loin les requêtes.

  5. #5
    Membre très actif
    Homme Profil pro
    Chef de projet MOA
    Inscrit en
    Janvier 2011
    Messages
    457
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Chef de projet MOA
    Secteur : Finance

    Informations forums :
    Inscription : Janvier 2011
    Messages : 457
    Par défaut
    moi je prefere de faire une requete SQL ,
    tu as tester le temps de reponse du requete et le temps du reponse du boucle for?

  6. #6
    Expert confirmé Avatar de Graffito
    Profil pro
    Inscrit en
    Janvier 2006
    Messages
    5 993
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2006
    Messages : 5 993
    Par défaut
    Le test mérite d'être fait
    +1
    et donnera gagnantes de très loin les requêtes.
    Si tu as chargé toutes les données dans une Datable en mémoire bindée au dataGridView (et que tu utilises la propriété Filter du binding source pour afficher des extraits de la table), la boucle For sera me semble t'il plus efficace, surtout si le serveur est sur une autre machine.

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    166
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Octobre 2010
    Messages : 166
    Par défaut
    Bonjour

    Merci pour vos réponses nocturnes ...

    la tendance est donc à la requête, mais je vais essayer de faire un chrono entre les 2 pour voir.

    Si tu as chargé toutes les données dans une Datable en mémoire bindée au dataGridView
    heu je sais pas , j'ai un truc du genre :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
                    DataTable data = new DataTable();
     
                    SqlCeDataAdapter da = new SqlCeDataAdapter(maRequeteSelect, connectionString);
     
                    da.Fill(data);
    sinon un "SELECT COUNT(*)" est plus rapide qu'un "SELECT *" ? ca demande moins de ressources ?

    @++

    KaloOopS

  8. #8
    Membre Expert
    Avatar de jbrasselet
    Homme Profil pro
    Chef de projet NTIC
    Inscrit en
    Mars 2006
    Messages
    1 022
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 47
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Chef de projet NTIC
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 022
    Par défaut
    Je le ferais par requête pour une simple et bonne raison : je ne ramènerais certainement pas mes milliers de lignes dans ma datatable !

    Si tu les affiches, je te conseille de mettre un pager et de ne ramener que celles qui seront affichées par la page en cours. Ensuite tu fais tes deux count pour avoir tes compteurs de pièces valides ou défectueuses.

  9. #9
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 259
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 259
    Par défaut
    sinon un "SELECT COUNT(*)" est plus rapide qu'un "SELECT *" ? ca demande moins de ressources ?
    Un SELECT * te renverra toutes les données correspondant au filtre. Tu serais obliger de faire une boucle (for par exemple) derrière pour les compter.

    Le SELECT COUNT(*) ne te renverra qu'une seule valeur qui sera justement le compte du nombre de données ramenées par la requete correspondante, SELECT * dans ce cas là.
    Le compte étant fait par le moteur SQL sans avoir besoin de charger l'ensemble des données, il sera de toute façon toujours plus rapide que tout autre solution qui nécessite un chargement des données en mémoire fusse-t-elle un BindingSource

  10. #10
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 204
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 204
    Par défaut
    d'une manière générale, quand on utilise un outil il est bien de connaitre le fonctionnement de celui ci

    après il suffit de réfléchir à ce qui se passe dans chaque cas, en schématisant ca donne :

    - select * de 1000 lignes avec 10 colonnes ramené dans un datagridview
    il y a 10000 choses à lire, à faire transiter entre sql server et vb, et à écrire dans vb
    ensuite faire une boucle sur les 1000 lignes pour compter le nombre qui est en défaut fait encore 1000 choses à lire
    donc au final 11000 lectures et 10000 écritures

    - select count(*) (...) where defaut
    parcours des 1000 lignes directement dans la table, donc seulement 1000 choses à lire
    si en plus tu mets un index sur la colonne defaut, le nombre de choses à lire peut tomber aux alentours de 10 je pense

    donc d'un coté 10 e/s et de l'autre 20000 e/s
    je pense que c'est vite vu ...


    si par contre tu as besoin d'afficher toutes les lignes, tu peux te contenter de lire dans le datatable on est plus à ca près ^^
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  11. #11
    Membre expérimenté
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Par défaut
    On à déja un peu tout dis mais je vais essayer de faire une synthese

    1- Si seul le compte t'intéresse, alors il est infiniment preferable de faire deux select

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    select Count(*) from matable where monfiltre=0
    select Count(*) from matable where monfiltre=1
    Ou meme
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    select Count(*),Monfiltre from matable Group by Monfiltre
    2- Si par contre tu tois Afficher des rows relatifs a monfiltre=1 ou 0, alors tu peux faire

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    DataView dvData=new DataView(data);
    dvData.RowFilter="Monfiltre=0";
    Du coup le count de dvData s'ajuste automatiquemen et si tu met dvData en datasource d'un DGV tu affiche les row voulu;

    Bien entendu, tu peux a tout moment adapter la valeur de RowFiler

  12. #12
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    166
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Octobre 2010
    Messages : 166
    Par défaut
    Heu question bête, je fais comment pour récupérer la valeur du "select count(*)"

    car si je fais un
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
                    DataTable data = new DataTable();
     
                    SqlCeDataAdapter da = new SqlCeDataAdapter(maRequeteSelect, connectionString);
     
                    da.Fill(data);
     
                    int result = data.Rows.Count;
    ca me renvoie result = 1, normal y a qu'une réponse a ma requête, mais comment avoir sa valeur ?

    DataTable ou DataSet ?

    merci

    @++

    KaloOopS

  13. #13
    Membre expérimenté
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Par défaut
    Un exemple tout chaud et emballé !
    A toi de completer

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
       // *********************************************************************************
        internal Object ExecuteScalar(string sql)
        {
          SqlConnection conn = OpenConnection();
          if (conn == null)
          {
            return null;
          }
     
          SqlCommand cmd = new SqlCommand(sql, conn);
     
          object O;
          try
          {
            O = cmd.ExecuteScalar();
          }
          catch (SqlException e)
          {
            this.isError = true;
            Message = e.Message;
            conn.Close();
            return null;
          }
          conn.Close();
          return O;
        }

  14. #14
    Expert éminent Avatar de Pol63
    Homme Profil pro
    .NET / SQL SERVER
    Inscrit en
    Avril 2007
    Messages
    14 204
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Puy de Dôme (Auvergne)

    Informations professionnelles :
    Activité : .NET / SQL SERVER

    Informations forums :
    Inscription : Avril 2007
    Messages : 14 204
    Par défaut
    il n'y a pas que les datasets et datatables dans la vie !

    il y a des objets plus bas niveau comme dbcommand et dbdatareader, qui sont utilisées par le dataadapter
    Cours complets, tutos et autres FAQ ici : C# - VB.NET

  15. #15
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    166
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Octobre 2010
    Messages : 166
    Par défaut
    Olibara comment tu fais marcher ton code avec une base SqlCe ?

    Pol :
    si vous ne comprenez pas ce que je dis, demandez à google
    vais suivre les conseils de ta signature car je ne comprends pas ... pour l'instant

    si quelqu'un pouvait m'expliquer avec un bout de code "simple" comment récupérer la valeur d'un SELECT COUNT(*) dans un INT avec une base SqlCe, ce serait cool

    merci

    @++

    KaloOopS

  16. #16
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 259
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 259
    Par défaut
    Je n'avais pas fait attention au code d'Olibara, je pensais qu'il était bon, mais effectivement je ne sais pas trop d'où il sort ce code.


    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
            SqlCeConnection cnn = new SqlCeConnection(connexionstring);
            SqlCeCommand cmd = cnn.CreateCommand();
            cmd.CommandType = CommandType.Text;
            cmd.CommandText = "SELECT COUNT(*) FROM matable WHERE monfiltre=0";
     
            cnn.Open();
     
            int32 Resultat = Convert.ToInt32(cmd.ExecuteScalar());
     
            cnn.Close();
            cmd.Dispose();
            cnn.Dispose();
    La méthode ExecuteScalar, renvoie uniquement la première colonne de la première ligne du jeu de résultat.
    Comme ici tu n'auras qu'une seule ligne d'une seule colonne, c'est plus rapide que de monter un dataReader.

  17. #17
    Membre expérimenté
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Par défaut
    Je n'avais pas fait attention au code d'Olibara, je pensais qu'il était bon, mais effectivement je ne sais pas trop d'où il sort ce code.
    Le code est bon et fonctionnel mais je n'avais pas vu qu'il s'agit de SQLCE

    J'avais aussi dit qu'il fallait completer pour le contexte, car je n'ai extrait qu'une petite methode d'une classe Wrapper de commandes SQL que j'ai fait pour usage aussi bien en MySql qu'en MsSql

    Et au passage j'utilise ce code tel quel dans une petite application mobile WindowsCE sans aucun souci

  18. #18
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2010
    Messages
    166
    Détails du profil
    Informations personnelles :
    Localisation : France, Rhône (Rhône Alpes)

    Informations forums :
    Inscription : Octobre 2010
    Messages : 166
    Par défaut
    Merci sevy ca fonctionne,

    désolé de créer un conflit entre vous avec mes questions

    cependant je suis à présent confronté à un étrange phénomène :

    j'ai ma table SqlCe qui a 1000 enregistrements de 11 colonnes (sans le id).

    j'ai fait des chronos pour tester quelle requête/quelle tâche est la plus rapide avec le code suivant (test sur SELECT COUNT(*), SELECT *, boucle FOR) :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
     
            private void button2_Click(object sender, EventArgs e)
            {
                DateTime start = DateTime.UtcNow; 
                SqlCeConnection cnn = new SqlCeConnection(connectionString);
                SqlCeCommand cmd = cnn.CreateCommand();
                cnn.Open();
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "SELECT COUNT(*) FROM " + tblSQL + " WHERE defaut = 0";
                nbPieceOK = Convert.ToInt32(cmd.ExecuteScalar());
                cmd.CommandText = "SELECT COUNT(*) FROM " + tblSQL + " WHERE defaut <> 0";
                nbPieceNOK = Convert.ToInt32(cmd.ExecuteScalar());
                cnn.Close();
                cmd.Dispose();
                cnn.Dispose();
                DateTime end = DateTime.UtcNow;
                double diff = diffTime(start, end);
                listBox1.Items.Add(diff);
     
                start = DateTime.UtcNow; 
                nbPieceOK = ExecuteSelect("SELECT * FROM " + tblSQL +" WHERE defaut = 0", false, data, "");
                nbPieceNOK = ExecuteSelect("SELECT * FROM " + tblSQL + " WHERE defaut <> 0", false, data, "");
                end = DateTime.UtcNow;
                diff = diffTime(start, end);
                listBox1.Items.Add(diff);
     
                start = DateTime.UtcNow;
                for (int i = 0; i < dgView1.Rows.Count; i++)
                {
                    int defautValue = Convert.ToInt16(dgView1.Rows[i].Cells[9].Value.ToString());
                    if (defautValue == 0)
                        nbPieceOK++;
                    else
                        nbPieceNOK++;
                }
                end = DateTime.UtcNow;
                diff = diffTime(start, end);
                 listBox1.Items.Add(diff);
       }
    la fonction pour faire le chrono est :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
            private double diffTime(DateTime start, DateTime end)
            {
                double result = 0;
                long diff = end.Ticks - start.Ticks;
                result = (diff / 10000.0);
                return result;
            }
    le "ExecuteSelect" est une fonction faisant un select * from ... via SqlCeDataAdapter et DataTable.

    les chronos sont affichés dans une listbox, et là le verdict est vraiment étonnant !!!

    avec mon PC (AMD 3000+, 1.5 Go RAM, XP SP3), je suis pour 1000 enregistrements entre :
    - 15.625 et 31.25 ms pour le SELECT COUNT(*)
    - 46.875 et 78.125 ms pour le SELECT *

    mais je suis entre :

    - 0 et 15.625 ms pour la boucle FOR !!!!

    étonnant non ?? ca veut dire que la boucle FOR est plus rapide que les requêtes SQL, si le datagridview est déjà rempli et visible.

    allez question subsidiaire :

    pourquoi mes chronos sont toujours des multiples de 15.625 ms ?? est-ce lié au performances du PC ou bien est-ce le DateTime.UtcNow qui n'est pas plus précis ? (j'ai le même multiple si je fais DateTime.Now).

    Merci

    @++

    KaloOopS

  19. #19
    Membre expérimenté
    Profil pro
    Mangeur de gauffre
    Inscrit en
    Octobre 2007
    Messages
    4 413
    Détails du profil
    Informations personnelles :
    Localisation : Belgique

    Informations professionnelles :
    Activité : Mangeur de gauffre

    Informations forums :
    Inscription : Octobre 2007
    Messages : 4 413
    Par défaut
    Tout a fait normal que ta boucle for soit plus rapide qu'une requete SQL
    Mais de toute facon tu dois ajouter au temps de cette boucle le temps SQL nécessaire a charger les donnée utiles a la boucle

    D'autre par l'erreur classique et récurrente est de considerer le datagridview comme un container de données permettant toute les manipulations

    Un datagridview est AVANT TOUT une vue !
    Les manipulations programatiques doivent se faire sur le modele de donnée sous-jacent (datatable, liste, etc) !

  20. #20
    Modérateur
    Avatar de sevyc64
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2007
    Messages
    10 259
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Janvier 2007
    Messages : 10 259
    Par défaut
    Pas de conflit, j'avais d'abord cru que son code était bon mais quand tu as dit que tu ne le comprenais j'ai effectivement cru qu'il avait donné l'implémentation de ExecuteScalar plutôt qu'un exemple d'utilisation. Mais au final, il a donné un code proche de celui que j'ai donné ensuite.

    Pour tes tests, les valeurs pour le For ne sont pas juste car tu zappe toute la partie ouverture de la connexion, chargement des données dans de DataAdapter, etc ...
    Malgré cela, même uniquement pour la partie boucle For je m'attendait à des résultats plus élevés.

    Concernant ExecuteSelect, on ne sait pas comment est fait la fonction, mais si tu passe par un datareader et une boucle For, tu devrais être dans les mêmes temps qu'avec le DataAdapter.

    Concernant le 15,625ms, le chiffre est surprenant. Il devrait plutôt être de 18.2ms.
    18.2ms étant la valeur du tick d'horloge "temps réel", celle qui donne l'heure.

    Dans la réalité, je pense que ton SELECT COUNT(*) doit être en dessous des 15ms, mais par manque de précision de la mesure, tu n'a pas la valeur réelle.

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Boucle For dans une requete SQL
    Par laurent0 dans le forum Général Python
    Réponses: 2
    Dernier message: 10/08/2011, 17h41
  2. Boucle For & Requete SQL
    Par Friksstyle dans le forum Langage
    Réponses: 6
    Dernier message: 19/04/2011, 17h12
  3. [MySQL] Requetes SQL dans une boucle PHP
    Par Siteac dans le forum PHP & Base de données
    Réponses: 2
    Dernier message: 12/11/2006, 14h35
  4. Requete SQL dans boucle while
    Par vince_grenoblois dans le forum MS SQL Server
    Réponses: 18
    Dernier message: 04/08/2006, 20h41
  5. Réponses: 3
    Dernier message: 06/07/2004, 10h21

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo