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

Lazarus Pascal Discussion :

Chargement optimisé d'une DBGrid avec les connecteurs natifs MySQL [Lazarus]


Sujet :

Lazarus Pascal

  1. #1
    Membre actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 252
    Points : 272
    Points
    272
    Par défaut Chargement optimisé d'une DBGrid avec les connecteurs natifs MySQL
    Bonjour,

    j'aimerais afficher le contenu d'une table dans une dbGrid. J'ai besoin
    • de voir les dernières lignes immédiatement (disons les 30 dernières)
    • et que le reste se charge en tâche de fond.


    Quand j'ai arrêté Lazarus, j'avais un système très compliqué qui ne fonctionnait pas avec une dbGrid mais avec une stringGrid tout simplement parce que je chargeais les 30 premières lignes dans le thread principal et les lignes suivantes étaient ajoutées ensuite dans un thread secondaire.

    Normalement ce problème pourrait être résolu avec le fetchrow du TSQLery (en readonly) dans le thread principal. Et curieusement je n'arrive pas à la faire fonctionner. J'utilise un connecteur MySQL 5.6 (avec une libmysql.dll 5.6).

    Merci pour vos commentaires et votre aide. Cordialement AD.

  2. #2
    Membre expérimenté

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Septembre 2003
    Messages
    733
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2003
    Messages : 733
    Points : 1 668
    Points
    1 668
    Billets dans le blog
    8
    Par défaut
    Bonjour,

    FetchRow, à vérifier et à "profiler" au niveau Serveur de base de données, établit un Curseur sur la totalité des lignes vérifiant les critères de sélection de votre requête SQL.
    Si par exemple votre requête SQL, même avec un FetchRow égale à 30, porte sur de 2 millions de lignes, au niveau Serveur de base de données, il est fort probable qu'un Curseur soit ouvert sur les 2 millions de lignes respectant la clause de restriction de votre requette SQL (c.à.d. la clause WHERE) ! Certes les lignes transitent sur le réseau au fur et à mesure, en fonction de la demande, par paquet de 30 lignes, mais au niveau Serveur de base de données, comme expliqué ci-dessus, c'est une autre histoire qui se déroule !

    Le Curseur peut être unidirectionnel, se déplacement uniquement vers l'avant, ce qui réduit l'impact en termes des ressources utilisées, mais généralement les performances sont fortement dégradées.

    Les technique telle que FetchRow etc. peuvent être considérées comme de vraies fadaises (c.à.d. une véritable plaisanterie !) au vue de leur implémentation et de ce qui se passe réellement au niveau Serveur de base de données.

    Aussi, je vous conseille la pagination au niveau Serveur de base de données. Pagination que vous gérez vous-même au niveau Serveur en utilisant les clauses LIMIT et OFFSET dans votre requête SQL. Vous pouvez ensuite rajouter des boutons de navigation (suivant , précédent etc.. ). Il s'agit d'une technique largement utilisée dans les applications professionnelles.

    A+
    "Une idée mal écrite est une idée fausse !"
    http://hamid-mira.blogspot.com

  3. #3
    Membre actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 252
    Points : 272
    Points
    272
    Par défaut
    Bonjour,

    Merci pour votre réponse.
    Je constate effectivement ce que vous dites : Charger 1000 enregistrements de la table me prend moins d'une seconde. Avec un fetchrow de 1000 sur une table de 100 000, il me faut plus de 2 secondes (le curseur qui balaye). Sinon, pour charger la table d'une traite, j'ai besoin de 6 secondes. Ce qui corrobore parfaitement vos propos.

    Le problème c'est qu'une dbGrid ne peut se charger que par une seule requête. Voilà pourquoi j'avais effectivement opté pour une TStringGrid. Une première requête LIMIT 1000 dans le thread principal (affichage immédiat) et une seconde LIMIT OFFSET en tâche de fond (dans un thread) en affichage différé à volonté (par tranches de 1000 par exmple ou le retse en une fois. Le problème c'est qu'une TStringGrid présente beaucoup de lacunes : il faut implanter la multi-sélection, la persistance de l'affichage des images... que possède nativement une TdbGrid.

    Donc, il n'y a toujours pas de solution simple à mon problème sauf peut-être les composants TMS (que je possède d'ailleurs en Lazarus et Delphi). Leur Grid autant que je me souvienne dispose de la multi-sélection. Si j'ai le temps, je regarderai.

    Merci pour votre aide. Cordialement. AD.

  4. #4
    Membre expérimenté

    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Septembre 2003
    Messages
    733
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2003
    Messages : 733
    Points : 1 668
    Points
    1 668
    Billets dans le blog
    8
    Par défaut
    Bonjour,
    Je ne crois pas que le problème ou les limitations que vous évoquez proviennent de TStringGrid vs TDBGrid.
    Vous pouvez très bien opter pour une architecture à plusieurs niveaux en utilisant par exemple plusieurs (enfin 2 ou 3) composants TMemDataset (onglet DataAccess) puis rattacher votre DBGrid à composant TMemDataSet représentant la page en cours.
    Vous pouvez ainsi anticiper le chargement de la page suivante dans un Thread secondaire et récupérer le résultat toujours dans un composant TMemDataSet.
    Votre DBGrid étant rattaché à un Datasource lui-même rattaché à un TMemDataSete. Vous pouvez donc en fonction de la page demandée (suivante ou précédente) "router" le TDataSource de la grille vers le TMemDataset idoine.

    Chacun des TMemDataset sera chargé à partir d'un vrai TDataset (TSQLQuery etc) basé sur une vraie requête SQL mettant en jeux LIMIT et OFFSET
    Dans le scénario que je décris, vous aurez en mémoire au plus 3 pages (page précédente, page en cours et page suivante) chacune "mappée" à un composant TMemDataset). Ces pages et composants TMemDataset, vous l'avez deviné, seront utilisées de manière circulaire (recyclées). vous n'aurez jamais la totalité des lignes de la table en mémoires ce qui serait déconseillé.

    A+
    "Une idée mal écrite est une idée fausse !"
    http://hamid-mira.blogspot.com

  5. #5
    Membre actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 252
    Points : 272
    Points
    272
    Par défaut
    Rebonjour,

    Oui, oui. Je n'avais pas pensé à cela. J'essaye.

    Encore merci. Cordialement. AD.

  6. #6
    Membre actif
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2015
    Messages
    252
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ille et Vilaine (Bretagne)

    Informations professionnelles :
    Activité : Développeur informatique

    Informations forums :
    Inscription : Septembre 2015
    Messages : 252
    Points : 272
    Points
    272
    Par défaut
    Bonjour,

    pendant la semaine, je me suis à nouveau penché sur la question. La méthode proposée par Hmira fonctionne bien.... Et pour faire une recherche, c'est tout à fait jouable même en utilisant la "pagination". Cela impose par contre une programmation plus compliquée qu'une approche classique.

    En Qt, malheureusement, il n'existe pas de DataSet donc le chargement se fait totalement en mémoire mais avec les threads on n'obtient le résultat escompté ou alors on utilise l'approche de Hmira.
    Cependant, j'avais un exemple précis avec Windev où sur la même table, j'avais un affichage instantané dans une "table fichier", l'équivalent d'une dbGrid.
    J'ai repris une telle approche classique avec uniDac et cela fonctionne correctement avec une petite astuce que j'avais essayée sur un SQLquery natif et qui ne fonctionne pas. Voici le premier de code de test qui m'a permis de vérifier les latences :

    Je me connecte :
    begin
    try
    UniConnection1.connected:= True;
    UniQuery1.open;
    Timer1.Enabled:= True;
    except
    Showmessage('oups');
    end;
    end;
    L'affichage dans la dbGrid des 100 premiers enregistrements déclarés dans Fetchrow est instantané. C'est cette partie qui fonctionne mal avec les TSQLQuery. Au lieu d'être quasi-instantané, l'affichage requiert 2 secondes.

    Mais contrairement à Windev qui continue automatiquement l'affichage de la table en tâche de fond sans gêner l'utilisateur, si l'on veut obtenir la suite du chargement avec un TuniQuery, il faut emmener le scrollbar vertical en bas de la dbGrid et.. attendre son actualisation : La suite du chargement ne s'effectue que si l'utilisateur la "demande". Ce n'est pas idiot, cela dispense du problème de la fin du remplissage... si elle n'est pas nécessaire , mais ce n'est pas ce que je veux. Le TSLQuery a le même comportement. C'est peu ergonomique : dans mon cas, j'appelle cette actualisation dès la fin du chargement des 100 premiers.

    Après avoir ouvert le TUniQuery, je lance le timer qui déclenche la fin de la procédure de chargement de la dbGrid.
    procedure TForm1.Timer1Timer(Sender: TObject);
    begin
    Uniquery1.last();
    Timer1.Enabled:= False;
    end;
    Évidemment ce n'est que le principe. Je l'ai amélioré au niveau de son ergonomie et du travail en tâche de fond.

    Cordialement. AD.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 29/10/2008, 10h46
  2. Realisation d'une pause avec les thread
    Par toitoine01 dans le forum Concurrence et multi-thread
    Réponses: 9
    Dernier message: 10/05/2006, 08h51
  3. une erreur avec les pointeur..
    Par lecyberax dans le forum C
    Réponses: 12
    Dernier message: 15/04/2006, 12h04
  4. Réponses: 3
    Dernier message: 09/04/2006, 12h58
  5. Réponses: 2
    Dernier message: 26/07/2004, 13h34

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