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

Bases de données Delphi Discussion :

[D7] Tracker de requête


Sujet :

Bases de données Delphi

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    141
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 141
    Points : 40
    Points
    40
    Par défaut [D7] Tracker de requête
    Bonjour,

    J'ai un projet en Delphi 7 assez conséquent et je cherche s'il existe un outil qui me permettrait de retrouver toutes les requêtes delete qui pourraient être lancées sur la base de données via le code (ADOCommand, delete sur dataset...)
    est ce que ce type d'outil existe ?

    J'ai actuellement des lignes qui se suppriment de ma base de données et je n'arrive pas à trouver comment.
    Ce qui est sûr c'est que la suppression ne provient pas des procédure stockées sur ma base de données donc surement une requête qui doit se lancer sur le code mais j'ai beau chercher je ne vois pas.

    Par avance, merci de vos retours.

  2. #2
    Membre expert
    Avatar de pprem
    Homme Profil pro
    MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Inscrit en
    Juin 2013
    Messages
    1 876
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loiret (Centre)

    Informations professionnelles :
    Activité : MVP Embarcadero - formateur&développeur Delphi, PHP et JS
    Secteur : High Tech - Multimédia et Internet

    Informations forums :
    Inscription : Juin 2013
    Messages : 1 876
    Points : 3 611
    Points
    3 611
    Par défaut
    bonjour

    une simple recherche sur "delete" dans les sources du projets (PAS et DFM) ne donne rien ?

  3. #3
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 429
    Points : 24 794
    Points
    24 794
    Par défaut
    Commence par les outils du SGBD, tu n'a pas pensé à le citer, quel meilleur endroit pour un moniteur SQL que le serveur !


    Sinon, tu peux aussi tricher en déclarant dans TOUTES les unités suspectées des classes de substution
    C'est moche, faut dupliquer le code comme premières classes et l'implémentation partout, pour remplacer le Delete du TDataSet et les Execute pour le SQL

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    TADOCommand = class(ADODB.TADOCommand)
    public
      function Execute(): _Recordset; 
    end;
     
    TADOQuery = class(ADODB.TADOQuery )
    public
      function ExecSQL(): Integer; 
    end;
    On peut d'ailleurs mettre le code interface et implementation dans deux fichiers INC inclut via {$I} pour simplifier le déploiement du code de débogage.
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  4. #4
    Membre chevronné Avatar de philnext
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    1 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 1 552
    Points : 1 780
    Points
    1 780
    Par défaut
    De manière générale c'est une bonne idée de mettre en place une possibilité de trace de toutes les requêtes, avec notamment le temps d'exécution, ça permet de tracer ce genre de problème mais aussi de vérifier les pb de perf.
    Bien sûr on met en paramètre le niveau de trace pour ne tracer que les requêtes en erreur en production ou tracer toutes les requêtes si on a un pb.
    Pour ça j'ai mis en place une fonction runRequete qui centralise toutes les requêtes.

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Septembre 2007
    Messages
    141
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2007
    Messages : 141
    Points : 40
    Points
    40
    Par défaut
    Bonjour,

    Merci pour vos réponses.

    J'ai lancé une recherche sur les .pas et non ça donne rien.
    Aucune requête delete sur dataset pouvant correspondre à ce qui se supprime sur ma base.
    comment je peux faire une recherche sur les .DFM ?

    Difficile de revenir sur toutes les unités étant suspectées car je ne sais vraiment pas laquelle génère le problème et c'est une table qui est mouvementée dans quasi tous les modules de l'application.
    Les lignes sont ajoutées avec succès et à un moment de la journée (plusieurs utilisateurs qui ne savent pas me dire après quelles manipulations) les lignes se suppriment.
    ça peut être une heure après ou bien en fin de journée ou encore le lendemain.

    Un tracking de toutes les requêtes avec temps d'exécution pourrait en effet m'aider à déterminer quel est le module qui lance la suppression.
    Auriez vous la possibilité de me fournir le code de votre fonction runRequete ?

  6. #6
    Membre émérite
    Avatar de ALWEBER
    Homme Profil pro
    Expert Delphi
    Inscrit en
    Mars 2006
    Messages
    1 483
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 68
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Expert Delphi

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 483
    Points : 2 734
    Points
    2 734
    Billets dans le blog
    10
    Par défaut Tracing avec Firedac
    Tu as un moniteur sous Firedac
    https://docwiki.embarcadero.com/RADS...ring_(FireDAC)
    Je l'ai utilisé il y a quelques années. Si besoin je peux retrouver un exemple

  7. #7
    Expert éminent sénior
    Avatar de ShaiLeTroll
    Homme Profil pro
    Développeur C++\Delphi
    Inscrit en
    Juillet 2006
    Messages
    13 429
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : Développeur C++\Delphi
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juillet 2006
    Messages : 13 429
    Points : 24 794
    Points
    24 794
    Par défaut
    Citation Envoyé par Abac_Angelique Voir le message
    Difficile de revenir sur toutes les unités étant suspectées car je ne sais vraiment pas laquelle génère le problème et c'est une table qui est mouvementée dans quasi tous les modules de l'application.
    Les lignes sont ajoutées avec succès et à un moment de la journée (plusieurs utilisateurs qui ne savent pas me dire après quelles manipulations) les lignes se suppriment.
    ça peut être une heure après ou bien en fin de journée ou encore le lendemain.

    Vous utilisez bien des transactions explicites ? vous avez penser à vos accès concurrentiels avec l'utilisation d'une PK AutoInc, une Identity ou une Sequence ... selon le SGBD qui n'a toujours pas été mentionné.
    C'est bien des DELETE en SQL qui sont le sujet ?
    Pas de Trigger ?

    Sinon, avez-vous essayez l'astuce des fichiers INC pour faire une substitution de classe, en généralisant le patch à chaque unité, il n'y aura plus de question à se poser.
    Cela évite dans un premier temps de réécrire tous les Open/ExecSQL, c'est via la substitution que vous injectez le code de monitoring, d'ailleurs, attention au multi-thread si vous utilisez un seul fichier log simultanément.

    le SQL dans une DFM, souvent difficile à retrouver avec l'alignement à 76 caractères par ligne du SQL dans une TStrings sérialisées
    Je préfère de loin un SQL dans le code pour les petits SQL, se faire un outil qui transforme un SQL en SQL Delphisé et réciproquement est utile, TOAD par le propose nativement pour Oracle et Delphi
    Sinon, les SQL complexes dans un fichiers Ressources.


    Comme expliqué, dans un fichier INC, ajouter une substitution du TADOQuery ou TADOCommand ... faut mettre cela {$I} dans la partie Interface
    Récemment, j'ai injecté une mesure de temps à un projet ... fait rare, j'ai utilisé directement TADOQuery et j'avais ajouté un Helper mais tout est dans une seule unité métier car c'est un petit outil interne.
    Dans un projet d'envergure, je fais toujours une classe par exemple TMyADOQuery qui encaspule le TADOQuery ... ah oui jamais de RAD, c'est un piège le RAD, mieux vaut allocation dynamique pour les objets DB et gérer finement le cycle de vie et la préparation/binding des requêtes.

    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
      { TADOQuery }
     
      TADOQuery = class(Data.Win.ADODB.TADOQuery)
      strict private
        class var
          FSQLLogFileName: string;
      private
        FContext: TConfigContext;
        FServer: string;
        FCatalog: string;
        FSchema: string;
      public
        constructor Create(AContext: TConfigContext); reintroduce; overload;
        constructor Create(const AServer: string; const ACatalog: string = ''; const ASchema: string = ''); reintroduce; overload;
     
        procedure Open();
        function ExecSQL: Integer;
      public
        class function GetSQLLogFileName(): string;
      end;
    un autre fichier INC dans la partie implémentation, vous faites cela sans reflechir, vous le systèmatisez à toute vos unités.

    Code D10, TStopwatch = QueryPerformanceCounter (ou au pire GetTickCount)

    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
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
     
    { TADOQuery }
     
    //------------------------------------------------------------------------------
    constructor TADOQuery.Create(AContext: TConfigContext);
    begin
      Create(AContext.Server, AContext.Catalog, AContext.Schema);
     
      FContext := AContext;
    end;
     
    //------------------------------------------------------------------------------
    constructor TADOQuery.Create(const AServer: string; const ACatalog: string = ''; const ASchema: string = '');
    begin
      inherited Create(nil);
     
      FServer := AServer;
      FCatalog := ACatalog;
      FSchema := ASchema;
    end;
     
    //------------------------------------------------------------------------------
    function TADOQuery.ExecSQL(): Integer;
    var
      Chrono: TStopwatch;
    begin
      Chrono := TStopwatch.StartNew();
     
      try
        Result := inherited ExecSQL();
      except
        on E: EOleException do
        begin
          if (E.ErrorCode = Winapi.Windows.E_FAIL) and Assigned(FContext) then
          begin
            // Pas de reconnexion pour un ExecSQL !
            if ccoAutoReconnectIfDisconnected in FContext.ConnectionOptions then
              Connection.Connected := False;
          end;
     
          raise;
        end;
      end;
     
      // Log uniquement les requêtes qui ont réussi (sans Exception)
      Chrono.Stop();
      TADOQueryHelper.TSQLLogger.Instance.Push(FServer, FCatalog, FSchema, SQL, Round(Chrono.ElapsedTicks / Chrono.Frequency * 1000000));
    end;
     
    //------------------------------------------------------------------------------
    procedure TADOQuery.Open();
    var
      Chrono: TStopwatch;
    begin
      Chrono := TStopwatch.StartNew();
     
      try
        inherited Open();
      except
        on E: EOleException do
        begin
          if (E.ErrorCode = Winapi.Windows.E_FAIL) and Assigned(FContext) then
          begin
            if ccoAutoReconnectAfterOpenError in FContext.ConnectionOptions then
            begin
              if FContext.ReConnection(Connection) then
              begin
                inherited Open();
                Exit;
              end;
            end
            else if ccoAutoReconnectIfDisconnected in FContext.ConnectionOptions then
              Connection.Connected := False;
          end;
     
          raise;
        end;
      end;
     
      // Log uniquement les requêtes qui sont ouvertes (sans Exception ni Reconnexion)
      Chrono.Stop();
      TADOQueryHelper.TSQLLogger.Instance.Push(FServer, FCatalog, FSchema, SQL, Round(Chrono.ElapsedTicks / Chrono.Frequency * 1000000));
    end;



    Jusqu'à présent dans ce projet, un Helper suffisait, mais on peut coupler Helper et classe de substitution si l'on fait cela dans le bon ordre, évidemement c'est hors sujet en D7, les Helper n'existent pas !

    Les classes imbriquées ça n'existe pas non plus d'ailleurs, et puis un Thread et une liste chaine pour le fun ... on peut faire plus simple mais ce n'est pas drole

    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
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    { TADOQueryHelper }
     
    type
      TADOQueryHelper = class helper for TADOQuery
      public
        type
          TSQLLogger = class(TThread)
          strict private
            type
              PSQL = ^TSQL;
              TSQL = record
                Server: string;
                Catalog: string;
                Schema: string;
                SQLText: string;
                Duration: Cardinal;
                Next: PSQL;
              end;
          strict private
            FFileName: string;
            FFirst: PSQL;
            FLast: PSQL;
            FLock: TCriticalSection;
            FSignal: TEvent;
     
            procedure Treat();
          protected
            procedure TerminatedSet(); override;
            procedure Execute(); override;
          public
            constructor Create();
            destructor Destroy(); override;
     
            procedure Clear();
            function Push(const AServer: string; ACatalog: string; const ASchema: string; const ASQL: TStrings; const ADuration: Cardinal): Boolean;
            function Peek(out AServer: string; out ACatalog: string; out ASchema: string; out ASQL: string; out ADuration: Cardinal): Boolean;
            function Pop(): Boolean;
        public
          // Singleton : Point d'Accès unique du TSQLLogger
          class function GetInstance(): TSQLLogger; static;
          class property Instance: TSQLLogger read GetInstance;
        end;
     
      public
        procedure Bind(const AParamName: string; const AParamValue: string; const AParamDirection: TParameterDirection = pdInput); overload;
        procedure Bind(const AParamName: string; const AParamValue: Variant; const AParamDateType: TDataType; const AParamDirection: TParameterDirection = pdInput); overload;
        procedure BindNull(const AParamName: string; const AParamDateType: TDataType; const AParamDirection: TParameterDirection = pdInput);
        procedure BindNullIfNotValue(const AParamName: string; const AParamDateType: TDataType; const AParamDirection: TParameterDirection = pdInput);
     
        function ItSelf(): TADOQuery;
      end;
    Aide via F1 - FAQ - Guide du développeur Delphi devant un problème - Pensez-y !
    Attention Troll Méchant !
    "Quand un homme a faim, mieux vaut lui apprendre à pêcher que de lui donner un poisson" Confucius
    Mieux vaut se taire et paraître idiot, Que l'ouvrir et de le confirmer !
    L'ignorance n'excuse pas la médiocrité !

    L'expérience, c'est le nom que chacun donne à ses erreurs. (Oscar Wilde)
    Il faut avoir le courage de se tromper et d'apprendre de ses erreurs

  8. #8
    Membre chevronné Avatar de philnext
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    1 552
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 1 552
    Points : 1 780
    Points
    1 780
    Par défaut
    Citation Envoyé par Abac_Angelique Voir le message
    Bonjour,

    Merci pour vos réponses.

    J'ai lancé une recherche sur les .pas et non ça donne rien.
    Aucune requête delete sur dataset pouvant correspondre à ce qui se supprime sur ma base.
    comment je peux faire une recherche sur les .DFM ?

    Difficile de revenir sur toutes les unités étant suspectées car je ne sais vraiment pas laquelle génère le problème et c'est une table qui est mouvementée dans quasi tous les modules de l'application.
    Les lignes sont ajoutées avec succès et à un moment de la journée (plusieurs utilisateurs qui ne savent pas me dire après quelles manipulations) les lignes se suppriment.
    ça peut être une heure après ou bien en fin de journée ou encore le lendemain.

    Un tracking de toutes les requêtes avec temps d'exécution pourrait en effet m'aider à déterminer quel est le module qui lance la suppression.
    Auriez vous la possibilité de me fournir le code de votre fonction runRequete ?
    Bon ça t'oblige à passer toutes tes requêtes par cette fonction (ou une qui ressemble) chez moi ça ressemble à ça :
    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
    function runReq (const sReq: string;  FDQuery: TFDQuery; FDCnx: TFDConnection): integer;
    var
      sMessage: string;
    begin
      sMessage := '';
      try
        try
          if FDCnx.Connected = False then
            FDCnx.Connected := True;
        // Requetes qui renvoient quelque chose:
          if (Pos('SELECT', UpperCase(sReq)) = 1) and (Pos('INTO', UpperCase(sReq)) = 0) and (Pos('UPDATE', UpperCase(sReq)) = 0) then
          begin
            FDQuery.Connection := FDCnx;
            FDQuery.SQL.Text := sReq;
            FDQuery.Open;
            Result := FDQuery.RecordCount;
          end
          else
            Result := FDCnx.ExecSQL(sReq);
     
     
        except
          on E: Exception do
          begin
            sMessage := E.Message;
            if bTransaction and (sReq <> '') then
              FDCnx.Rollback;
            Result := -2;
          end;
        end;
      finally
        if Result < 0 then
          TraceError('>' + sReq + #13 + inttostr(Result))
        else
          TraceInfo('!' + sReq + #13 + inttostr(Result) + #13 + sMessage);
      end;
    end;
    Tu peux y rajouter du code pour vérifier le temps d'exécution de la requête, ça permet de tracer les perf. voire du code pour modifier la requête avant exécution, pour l'adapter à un type de base de données ou autre.

Discussions similaires

  1. Comment tracker (Voir) la requête sql qui envoyée vers la base
    Par devalender dans le forum MkFramework
    Réponses: 2
    Dernier message: 04/11/2014, 23h47
  2. Utilisation de MAX dans une requête SQL
    Par Evil onE dans le forum Langage SQL
    Réponses: 7
    Dernier message: 15/06/2004, 19h38
  3. Requete requête sous sybase
    Par eddie dans le forum Sybase
    Réponses: 3
    Dernier message: 02/04/2003, 15h51
  4. Requête imbriquée et indexes INTERBASE
    Par vadim dans le forum InterBase
    Réponses: 2
    Dernier message: 06/09/2002, 17h15
  5. [BDD] Enregistrer le résultat d'une requête
    Par Mowgly dans le forum C++Builder
    Réponses: 5
    Dernier message: 19/06/2002, 16h26

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