Se connecter avec
S'enregistrer | Connectez-vous

Requête / dernier entrée avec clé primaire

Dernière réponse : dans Programmation

Bonjour à tous,

Je souhaite, dans une requête de sélection, ne faire apparaître que la dernière entrée d'une table.
Je vous explique un peu l'architecture.
La table [Tous sat] contient une clé primaire (donc sans doublon) qui est la colonne "Cospar".
Cette table est liée à une autre dénommée [Statut] qui contient également une colonne "Cospar" avec un liaison du type "left join". Dans cette table, "Cospar" n'est pas une clé primaire puisqu'un changement d'état peu intervenir et nécessiter la création d'une deuxième ligne portant le même numéro "Cospar".
Pour une question d'historisation, je souhaite conserver toutes les entrées et donc, des doublons.

Dans la requête exposée en fin de message, le résultat renvoie toutes les entrées de la table [statut] portant un "cospar" présent également dans la table [Tous sat].

Ce que je souhaite, c'est que seul la dernière entrée de la table [statut] pour un "Cospar" donné soit renvoyée.

J'espère que vous aurez compris et que vous pourrez m'aider.
Je reste à votre disposition pour toute information complémentaire dont vous pourriez avoir besoin pour résoudre mon problème.

Merci d'avance.

  1. SELECT [Tous sat].Cospar, [Tous sat].US, [Tous sat].Famille, [Tous sat].Nom, [Tous sat].Lancement, STATUT.STATUT, [Tous sat].Orbite
  2. FROM (([Tous sat] LEFT JOIN PTF ON [Tous sat].N°BASE = PTF.N°BASE) LEFT JOIN CU_Positionnement ON [Tous sat].N°BASE = CU_Positionnement.N°BASE) LEFT JOIN STATUT ON [Tous sat].N°BASE = STATUT.N°BASE
  3. WHERE ((([Tous sat].Lancement)<DateSerial(Year(Date()),Month(Date()),1-1)) AND ((STATUT.[DATE de validité])<Date()-41) AND (([Tous sat].Pays)="RUS") AND (([Tous sat].Missions)="P"))
  4. OR ((([Tous sat].Lancement)<DateSerial(Year(Date()),Month(Date()),1-1)) AND ((STATUT.[DATE de validité]) Is Null) AND (([Tous sat].Pays)="RUS") AND (([Tous sat].Missions)="P"))
  5. ORDER BY [Tous sat].Famille, [Tous sat].Lancement, STATUT.[DATE de validité] DESC;
Lassé par la pub ? Créez un compte

Meilleure solution

Expert Programmation

Je donne un nom à chacune de mes sous-requêtes : S1 et S2, grâce aux clauses AS.
J'aurais plus mettre Gui & Zeb, mais bon, pas terrible pour des sous-requêtes :/  ... ;) 

Ohhh, la clause HAVING est une très bonne idée !
C'est bizarre, j'aurais bien vu des >= ou des <= pour comparer tes dates. A voir.

(Revois ta clause WHERE, tu as deux fois la même chose. Et puis depuis le début, tu as un 1-1 plutôt original dans ton DateSerial :??:  )

Elis une meilleure réponse pour passer le topic en [résolu] si tu l'estimes justifié.
Expert Programmation

Mon Dieu, du SQL ACCESS, quel horreur !!! :peur: 

Salut guigillet :hello: 

La clef primaire de [Tous sat] est unique. Elle devient une clef étrangère de [Status] où elle n'est plus unique, c'est tout à fait normale ;) 

"La dernière", qu'est-ce que ça veut dire ? Tu veux dire "La plus récente" ?
Il te faut alors :
  • un champ Date dans la table [statut]
  • une clause GROUP BY pour prendre la plus grande des dates possibles

    -----------------------

    M'enfin comment peux-tu relire ce code :pfff: 

    1. SELECT [Tous sat].Cospar,
    2. [Tous sat].US,
    3. [Tous sat].Famille,
    4. [Tous sat].Nom,
    5. [Tous sat].Lancement,
    6. STATUT .STATUT,
    7. [Tous sat].Orbite
    8.  
    9. FROM (( [Tous sat]
    10. LEFT JOIN PTF ON [Tous sat].N°BASE = PTF.N°BASE )
    11. LEFT JOIN CU_Positionnement ON [Tous sat].N°BASE = CU_Positionnement.N°BASE )
    12. LEFT JOIN STATUT ON [Tous sat].N°BASE = STATUT.N°BASE
    13.  
    14. WHERE ( [Tous sat]. Lancement < DateSerial(Year(Date()),Month(Date()),1-1)
    15. AND STATUT .[DATE de validité] < Date() - 41
    16. AND [Tous sat].Pays = "RUS"
    17. AND [Tous sat].Missions = "P"
    18. )
    19. OR (
    20. [Tous sat]. Lancement < DateSerial(Year(Date()),Month(Date()),1-1)
    21. AND STATUT .[DATE de validité] Is Null
    22. AND [Tous sat]. Pays = "RUS"
    23. AND [Tous sat]. Missions = "P" )
    24.  
    25. ORDER BY [Tous sat]. Famille,
    26. [Tous sat]. Lancement,
    27. STATUT .[DATE de validité] DESC


    On peut savoir à quoi servent PTF et CU_Positionnement ? Je les retire pour la suite.
    Et si tu révisais un peu ton algèbre de Boole ! Tu as 8 conditions, 5 suffisent.
    Quant à la liaison entre tes deux tables, elle se fait sur N°BASE, pas sur Cospar.

    1. select [Tous sat].Cospar,
    2. [Tous sat].US,
    3. [Tous sat].Famille,
    4. [Tous sat].Nom,
    5. [Tous sat].Lancement,
    6. STATUT .STATUT,
    7. [Tous sat].Orbite
    8.  
    9. FROM [Tous sat] LEFT JOIN STATUT ON [Tous sat].N°BASE = STATUT.N°BASE
    10.  
    11. WHERE [Tous sat]. Lancement < DateSerial(Year(Date()),Month(Date()),1-1)
    12. AND [Tous sat]. Pays = "RUS"
    13. AND [Tous sat]. Missions = "P"
    14. AND STATUT .[DATE de validité] Is Null
    15. OR STATUT .[DATE de validité] < Date() - 41
    16.  
    17. ORDER BY [Tous sat]. Famille,
    18. [Tous sat]. Lancement,
    19. STATUT .[DATE de validité] DESC


    -----------------------------------------------------------------

    Bon, et bien maintenant que c'est lisible on s'aperçoit que les champs de la table STATUT utilisés sont : STATUT, N°BASE et [DATE de validité].
    Donc il suffit d'ajouter une clause GROUP BY sur les champs STATUT et N°BASE de la table STATUT, et d'utiliser Max([DATE de validité]) à la place de [DATE de validité].

    Je n'ai pas ACCESS sous la main pour vérifier. Ça marche ?

    Merci pour cette première réponse rapide.

    Je réponds point à point.

    - Par dernière, j'entends effectivement la plus récente.

    - Le champs date dans la table [STATUT] existe, il s'appelle [DATE de validité] mais n'est pas forcément renseigné.

    - En fait, le code, je ne le relis pas, peut-être à tort.
    Pour être franc, je sais que je vais faire hurler certains, je construis les requêtes avec l'assistant, d'où le risque qu'elles ne soient pas optimisées :heink: 

    - Les tables PTF et CU_Positionnement sont intégrées dans un formulaire qui reprend également d'autres tables, toutes liées à [Tous sat] par le champ N°BASE. Je te l'accorde, elles ne sont pas fondamentalement utiles ici ;) 

    - Au temps pour moi, la liaison est effectivement faite sur N°BASE :D 

    En retouchant un peu l'expression SQL, je tombe toujours sur le même résultat, à savoir que tous les enregistrements de STATUT.N°BASE apparaissent dans le résultat de la requête.

    Voici comment j'ai réécrit ta suggestion. J'espère ne pas avoir mal compris.

    1. GROUP BY [Tous sat].Famille, [Tous sat].Nom, [Tous sat].Lancement, STATUT.STATUT, [Tous sat].Orbite, STATUT.N°BASE
    2. HAVING (((Max(STATUT.[DATE de validité]))<>False Or (Max(STATUT.[DATE de validité])) Is Null))
    3. ORDER BY [Tous sat].Famille, [Tous sat].Lancement;




    Expert Programmation

    UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUH
    ¯¯/¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
    [:zeb:3]
    (Tu m'as bien fait hurler !)

    Nan, il faut ne faire le regroupement que pour la table STATUT.
    ---------------

    Bon, en progrzmmation (mais ça marche aussi pour pleins d'autres domaines), quand on a un gros problème, on le découpe en problèmes plus petits, et on les résout un par un, c'est plus long, mais plus facile.

    Commence par faire une requête sur STATUT qui ne renvoie que les champs nécessaires, à savoir la clef de [Tous sat], et la plus grande date, regroupé par clef, donc.
    1. SELECT nobase, Max(datevalid)
    2. FROM statut
    3. GROUP BY nobase

    A ceci on joint la même table pour avoir le statut correspondant :
    1. SELECT s2.no_base,
    2. s2.date_valid,
    3. s2.statut
    4. FROM
    5. ( SELECT no_base,
    6. MAX(date_valid) AS date_valid
    7. FROM statut
    8. GROUP BY no_base ) AS s1 INNER JOIN
    9. ( SELECT no_base,
    10. date_valid,
    11. statut
    12. FROM statut ) AS s2 ON s1.no_base = s2.no_base
    13. AND s1.date_valid = s2.date_valid

    Evidemment, le couple (no_base, date_valid) doit être unique.

    En joignant le résultat de cette requête avec TousSat, tu devrais obtenir ce que tu cherches.

    Euh ??? Je ne comprends pas d'où viennent ce "s2"et ce "s1".

    Qu'à cela ne tienne, tu m'as mis sur la bonne voie avec ton idée de Max([DATE de validité]).

    Donc, même si ça semble bien lourd et peu optimisé par rapport à ton code ci-dessus, je te joins mon code.

    1. SELECT [Tous sat].Famille,
    2. [Tous sat].Nom,
    3. [Tous sat].Lancement, STATUT.STATUT,
    4. [Tous sat].Orbite,
    5. STATUT.N°BASE,
    6. Max(STATUT.[DATE de validité]) AS [MaxDeDATE de validité]
    7.  
    8. FROM [Tous sat] LEFT JOIN STATUT ON [Tous sat].N°BASE = STATUT.N°BASE
    9.  
    10. WHERE ((([Tous sat].Lancement)<DateSerial(Year(Date()),Month(Date()),1-1))
    11. AND (([Tous sat].Pays)="RUS") AND (([Tous sat].Missions)="P"))
    12. OR ((([Tous sat].Lancement)<DateSerial(Year(Date()),Month(Date()),1-1))
    13. AND (([Tous sat].Pays)="RUS") AND (([Tous sat].Missions)="P"))
    14.  
    15. GROUP BY [Tous sat].Famille,
    16. [Tous sat].Nom,
    17. [Tous sat].Lancement,
    18. STATUT.STATUT,
    19. [Tous sat].Orbite,
    20. STATUT.N°BASE
    21.  
    22. HAVING ((([Tous sat].Lancement)<>Max([STATUT].[DATE de validité]) Or ([Tous sat].Lancement) Is Null)
    23. AND ((Max(STATUT.[DATE de validité]))<>False)) OR (((Max(STATUT.[DATE de validité])) Is Null))
    24.  
    25. ORDER BY [Tous sat].Famille, [Tous sat].Lancement;


    J'ai pas mal tâtonné mais cette requête me renvoie bien la plus récente entrée de [DATE de validité] qui correspond à un N°BASE donné en ne faisant pas apparaître les entrées précédentes.

    Merci pour ton aide, je n'aurai sans doute jamais pensé à Max([DATE de validité])

    Ok, j'ai maintenant compris pour les s1 et s2.

    Pour la clause HAVING, si je mets des >= ou <=, il y a des cas pour lesquels ça ne marche pas. J'ai fait différents essais et <> est le seul opérateur qui renvoie le nombre de résultats voulu.

    Merci pour la clause WHERE, je l'ai effectivement revue et grâce à toi, elle est maintenant grandement épurée.

    Le 1-1, c'est pour prendre le dernier jour du mois précédent celui dans lequel nous sommes.
    C'est le seul moyen que j'ai trouvé...peut-être en existe-t-il un plus simple ???

    Pour moi, le topic est résolu.

    Un grand MERCI encore pour ton aide.

    A bientôt peut-être.
    Lassé par la pub ? Créez un compte