Les requêtes sélectionnant les absents

Il s'agit ici de sélectionner les enregistrements d'une table absents d'une autre. Par exemple,on souhaite avoir la liste des 'clients' qui n'ont jamaispassé de commande.

Structure de notre table client:
Id_Client (clé primaire)
Nom_Client

Structure de notre table commande:
Id_Commande (clé primaire)
Id_Client
Date_Commande

La requête suivante nous permet d'avoir la liste des clients sans commande:

:alias:client.db| Id_Client  | Nom_Client |
                |Check _IdC!| Check      |

:alias.Commande.db | Id_Commande            | Id_Client|
                   |calc count, count = 0 | _IdC     |  


Bien sûr,, ce typede requête est plus ouvent utile en sélectionnant également sur la datede commande (les clients qui n'ont pas commandé depuis le début del'année) ou avec d'autres critères (montant de la commande, ...). On peut également supprimer de la table Clients lesenregistrements concernés.

Rermarque:  Nous utilisons le mot-clé anglais Count de préférence àNombre car il est compris de toutes les versions de Paradox (Us etégalement FR). Si bien sûr vous n'utilisez que la version FR,vous pouvez utiliser Nombre sans problème.

Nous avons expliqué dans la note "Utilisation des contraintes d'intégrité Paradox" pourquoi la gestion des contraintes d'intégrité directement par paradox était à éviter. Nous &nbspallons montré ici comment Programmer celles-ci dans les objets Cadres de table (que nous appelerons du diminutif TF pour TableFrame) et Multi-enregistrements (diminutif MR pour Mult-Records). Nous préconisons d'utiliser au maximum ces objets pour la mise en place de vos champs dans vos fiches car vous bénéficiez alors pleinement de la richesse du modèle évènementitel de Paradox.

Programmer des 'triggers'

Tout d'abord, il faut piéger les évènements susceptibles dé déclencher la vérification d'une contrainte d'intégrité. &nbspLe meilleur endroit est bien sûr la méthode intégrée 'action' des objets MR et TF. &nbspDans cette méthode nous nous intéresserons aux actions relatives aux données (classe DataAction). En fait, l'instruction switch sur la classe d'action est destinée uniquement à organiser le code et n'est pas indispensable dans le cas présent. &nbspNous allons ensuite piéger les différents évènements relatifs au verrouillage (qui se produit avant qu'une modification soit autorisée), au déverrouillage (lorsqu'on veut imputer (enregistrer) les modifications sur l'enregistrement, à l'insertion et à la suppression. Pour chaque évènement, nous poserons un piège (que l'on appelera trigger pour faire pro) avant que l'évènement ne se produise et un second après que l'évènement ne se soit produit.

 

Le trigger avant à pour rôle d'autoriser ou non l'opération. Le trigger après à pour rôle de compléter l'opération (qui a déjà été effectuée par Paradox). Nous donnons des noms significaitfs à nos triggers (toujours les mêmes, ça simplifie la vie) et l'on obtient le code suivant pour la méthode action des objets MR et TF:


method action(var eventInfo ActionEvent)
SWITCH
  CASE eventInfo.actionClass() = DataAction :
      SWITCH
         CASEnbsp;eventInfo.id() = DataUnlockRecord or eventInfo.id() =DataPostRecord:
           IF self.Touched then
             IF tbAvantImputer_LO() = FALSE THEN
                 DISABLEDEFAULT
                eventInfo.setErrorCode(canNotDepart)
                 RETURN
              ELSE
                 DODEFAULT
                IFeventInfo.errorCode() = 0 THEN
                    tbApresImputer_LO()
                  ENDIF
              ENDIF
            ENDIF
         CASE nbsp;eventInfo.id() = DataLockRecord :
            IF tbAvantVerrouiller_LO() = FALSE THEN
                 DISABLEDEFAULT
                eventInfo.setErrorCode(userError)
             ELSE
                 DODEFAULT
                IFeventInfo.errorCode() = 0 THEN
                    tbApresVerrouiller_LO()
                 ENDIF
             ENDIF
         CASE nbsp;eventInfo.id() = DataInsertRecord :
            IF tbAvantInserer_LO() = FALSE THEN
                 DISABLEDEFAULT
                eventInfo.setErrorCode(userError)
             ELSE
                 DODEFAULT
                IFeventInfo.errorCode() = 0 THEN
                    tbApresInserer_LO()
                 ENDIF
             ENDIF
          CASE nbsp;eventInfo.id() = DataDeleteRecord :
            IF tbAvantSupprimer_LO() = FALSE THEN
                 DISABLEDEFAULT
                eventInfo.setErrorCode(userError)
             ELSE
                 DODEFAULT
                IFeventInfo.errorCode() = 0 THEN
                    tbApresSupprimer_LO()
                 ENDIF
            ENDIF
      ENDSWITCH
  CASE eventInfo.actionClass() = EditAction :
  CASE eventInfo.actionClass() = FieldAction :
  CASE eventInfo.actionClass() = MoveAction :
  CASE eventInfo.actionClass() = SelectAction :
ENDSWITCH
endmethod

Puis (ou avant) nous renseignons les méthodes personnalisées tbAvantOperation_LO et tbApresOperation_LO que nous localisons également dans les objets TF et MR (cela permettra de les programmer de façon spécifique à chaque table). Le paramètre de retour indique le résultat de l'opération. &nbspPar exemple le retour de la méthode tbAvantInserer_LO permet de vérifier si l'insertion est permise. Un retour Tue autorisera l'opération alors qu'un retour False interdit l'opération.


method tbAvantInserer_LO() LOGICAL
  Valeur retour: FALSE si erreur dans la méthode ou si insertion non autorisée
   ....... votre programmation
 nbsp;RETURN TRUE
endMethod

Ainsi, on utilisera systématiquement les triggers Avant pour contrôler que l'opération est possible et les triggers Apres pour en tirer les conséquences. Il est important de bien comprendre la situation des triggers par rapport au Dodefault qui déclenche l'opération par Paradox.

 

Ainsi dans l'évènement DataInsertrecord, le Dodefault provoque l'insertion d'un enregistrement vierge (ou avec les valeurs par défaut pour la table). &nbspLa programmation dans tbApresInserer_LO a pour rôle de compléter cette insertion par défaut, par exemple en affectant un numéro d'incrément à un champ (cf note <link>Gestion des incréments) . A ce moment-là, l'utilisateur n'a pas encore effectué sa saisie.

 

En revanche, dans l'évènement DataDeleteRecord, DoDefault provoque la suppression physique de l'enregistrement. Si on veut effectuer des opérations suite à cette suppression, il est nécessaire de mémoriser les clés et autres informations qui seraient nécessaires dans tbAvantSupprimer_LO.