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 jamais
passé 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 type
de requête est plus ouvent utile en sélectionnant également sur la date
de commande (les clients qui n'ont pas commandé depuis le début de
l'année) ou avec d'autres critères (montant de la commande, ...).
On peut également supprimer de la table Clients les
enregistrements 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  allons 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é.  Le meilleur endroit est bien sûr la méthode intégrée 'action' des objets MR et TF.  Dans 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.  Nous 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
CASE
nbsp;eventInfo.id() = DataUnlockRecord or eventInfo.id() =
DataPostRecord: IF self.Touched then IF tbAvantImputer_LO() = FALSE THEN DISABLEDEFAULT
eventInfo.setErrorCode(canNotDepart) RETURN ELSE DODEFAULT
IF
eventInfo.errorCode() = 0 THEN
tbApresImputer_LO() ENDIF ENDIF ENDIF CASE nbsp;eventInfo.id() = DataLockRecord : IF tbAvantVerrouiller_LO() = FALSE THEN DISABLEDEFAULT
eventInfo.setErrorCode(userError) ELSE DODEFAULT
IF
eventInfo.errorCode() = 0 THEN
tbApresVerrouiller_LO() ENDIF ENDIF CASE nbsp;eventInfo.id() = DataInsertRecord : IF tbAvantInserer_LO() = FALSE THEN DISABLEDEFAULT
eventInfo.setErrorCode(userError) ELSE DODEFAULT
IF
eventInfo.errorCode() = 0 THEN
tbApresInserer_LO() ENDIF ENDIF CASE nbsp;eventInfo.id() = DataDeleteRecord : IF tbAvantSupprimer_LO() = FALSE THEN DISABLEDEFAULT
eventInfo.setErrorCode(userError) ELSE DODEFAULT
IF
eventInfo.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.  Par 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).  La 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 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. 
|