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

Langage SQL Discussion :

Courant ou dernier


Sujet :

Langage SQL

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre expérimenté
    Avatar de Kropernic
    Homme Profil pro
    Analyste / Programmeur / DBA
    Inscrit en
    Juillet 2006
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Analyste / Programmeur / DBA
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 932
    Par défaut Courant ou dernier
    Hello,

    Cela a très certainement déjà été demandé à de nombreuses reprises mais je ne parviens pas à mettre la main dessus.

    Considérons, par exemple, la table d'addresses suivantes (example simplifié pour focaliser le besoin):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    CREATE TABLE T_ADDRESS_ADR (	ADR_ID INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
    	CLI_ID INT NOT NULL REFERENCES T_CLIENT_CLI.CLI_ID,
    	ADR_START_DATE DATETIME2 NOT NULL,
    	ADR_END_DATE DATETIME2 NOT NULL DEFAULT '99991231',
    	ADR_LINE1 VARCHAR(500),
    	ADR_LINE2 VARCHAR(500),
    	ADR_LINE3 VARCHAR(500)
    )
    Le besoin est de récupérer l'adresse actuelle ou la dernière valable.

    Actuellement, j'y parviens à base de CTE et de la fonction de fenêtrage ROW_NUMBER.

    Cela donne quelque chose dans ce genre-ci :
    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
    WITH ADR_TMP AS (	SELECT	ADR_ID, CLI_ID, ADR_START_DATE, ADR_END_DATE, ADR_LINE1, ADR_LINE2, ADR_LINE3, ROW_NUMBER() OVER(PARTITION BY CLI_ID ORDER BY ADR_END_DATE DESC, ADR_START_DATE DESC) AS 'RN'
    	FROM	T_ADDRESS_ADR
    ),
    ADR AS (
    	SELECT	ADR_ID, CLI_ID, ADR_START_DATE, ADR_END_DATE, ADR_LINE1, ADR_LINE2, ADR_LINE3
    	FROM	T_ADDRESS_ADR
    	WHERE	GETDATE() BETWEEN ADR_START_DATE AND ADR_END_DATE
    	UNION
    	SELECT	T.ADR_ID, T.CLI_ID, T.ADR_START_DATE, T.ADR_END_DATE, T.ADR_LINE1, T.ADR_LINE2, T.ADR_LINE3
    	FROM	T_ADDRESS_ADR T
    				INNER JOIN ADR_TMP V
    					ON	T.ADR_ID = V.ADR_ID
    	WHERE	V.RN = 1
    )
     
     
    SELECT	*
    FROM	ADR
    WHERE	CLI_ID = XXX
    Cela fonctionne mais n'y aurait-il pas mieux ?

    Merci d'avance.

  2. #2
    Expert confirmé
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 373
    Détails du profil
    Informations personnelles :
    Âge : 48
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 373
    Billets dans le blog
    17
    Par défaut
    Cela ne revient pas à chercher pour ton client la ligne avec MAX(ADR_END_DATE) ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    SELECT TES_COLONNES
    FROM T_ADDRESS_ADR
    WHERE (CLI_ID, ADR_END_DATE) = (
    	SELECT CLI_ID, MAX(ADR_END_DATE)
    	FROM T_ADDRESS_ADR
    	WHERE CLI_ID = XXX
    	GROUP BY CLI_ID
    )
    D'ailleurs il faudra une contrainte d'unicité (CLI_ID, ADR_END_DATE).

  3. #3
    Membre expérimenté
    Avatar de Kropernic
    Homme Profil pro
    Analyste / Programmeur / DBA
    Inscrit en
    Juillet 2006
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Analyste / Programmeur / DBA
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 932
    Par défaut
    Non car on pourrait avoir une ligne pour une adresse qui commence dans le futur par exemple.

    N.B. : J'ai pris le cas des adresses pour l'example mais il y a le même problème avec tout un tas d'autres tables qui tournent autour du client où le problème est chaque fois d'avoir l'info courante ou la dernière valide.

  4. #4
    Expert confirmé
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 373
    Détails du profil
    Informations personnelles :
    Âge : 48
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 373
    Billets dans le blog
    17
    Par défaut
    Je ne comprends pas. Peux-tu donner un échantillon et le résultat attendu ?

  5. #5
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Sr. Specialist Solutions Architect @Databricks
    Inscrit en
    Septembre 2008
    Messages
    8 454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Sr. Specialist Solutions Architect @Databricks
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 454
    Par défaut
    Citation Envoyé par Séb. Voir le message
    Cela ne revient pas à chercher pour ton client la ligne avec MAX(ADR_END_DATE) ?
    Non, si il y a une adresse préremplie pour le futur, ça bloquera l'adresse courante.

    La requête initiale me paraît plutôt correcte, vous pouvez toutefois vous passer de la jointure entre ADR_TMP et T_ADRESSE puisque vous avez déjà tout dans ADR_TMP.
    Il est possible que dans ce cas un OR soit plus efficace que l'UNION (car les deux filtres me paraissent difficilement indexable) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    with CTE_ADR_TMP as
    (
    select ADR_ID, CLI_ID, ADR_START_DATE, ADR_END_DATE, ADR_LINE1, ADR_LINE2, ADR_LINE3, ADR_START_DATE, ADR_END_DATE
         , row_number() over(partition by CLI_ID order by ADR_END_DATE desc, ADR_START_DATE desc) as rn
      from T_ADDRESS_ADR
    )
    select ADR_ID, CLI_ID, ADR_START_DATE, ADR_END_DATE, ADR_LINE1, ADR_LINE2, ADR_LINE3
      from CTE_ADR_TMP
     where rn = 1
        or getdate() between ADR_START_DATE and ADR_END_DATE;

  6. #6
    Expert confirmé
    Avatar de Séb.
    Profil pro
    Inscrit en
    Mars 2005
    Messages
    5 373
    Détails du profil
    Informations personnelles :
    Âge : 48
    Localisation : France

    Informations professionnelles :
    Secteur : High Tech - Opérateur de télécommunications

    Informations forums :
    Inscription : Mars 2005
    Messages : 5 373
    Billets dans le blog
    17
    Par défaut
    Citation Envoyé par Waldar Voir le message
    Non, si il y a une adresse préremplie pour le futur, ça bloquera l'adresse courante.
    Dans ce cas si on ne regarde que les lignes dont ADR_START_DATE est < à la date courante ?

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    SELECT TES_COLONNES
    FROM T_ADDRESS_ADR
    WHERE (CLI_ID, ADR_END_DATE) = (
    	SELECT CLI_ID, MAX(ADR_END_DATE)
    	FROM T_ADDRESS_ADR
    	WHERE TRUE
                AND ADR_START_DATE < CURRENT_DATE
                AND CLI_ID = XXX
    	GROUP BY CLI_ID
    )
    Je serais plus à l'aise avec un échantillon

  7. #7
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Sr. Specialist Solutions Architect @Databricks
    Inscrit en
    Septembre 2008
    Messages
    8 454
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 48
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Sr. Specialist Solutions Architect @Databricks
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 454
    Par défaut
    Citation Envoyé par Séb. Voir le message
    Dans ce cas si on ne regarde que les lignes dont ADR_START_DATE est < à la date courante ?
    Dans ce cas là tu rates la dernière.

    L'énoncé m'est apparu assez clair :
    Le besoin est de récupérer l'adresse actuelle ou la dernière valable
    Si la dernière date est l'adresse actuelle, on récupère cette ligne. Si ce n'est pas la même on doit récupérer deux lignes:
    CLIENT1	ADR1	01/01/2000	31/12/2022	
    CLIENT1	ADR2	01/01/2023	31/12/9999	<= c'est l'adresse courante et la dernière
    
    CLIENT2	ADR1	01/01/2000	31/12/2022	
    CLIENT2	ADR2	01/01/2023	31/12/2023	<= c'est l'adresse courante
    CLIENT2	ADR3	01/01/2024	31/12/9999	<= c'est la dernière

  8. #8
    Membre expérimenté
    Avatar de Kropernic
    Homme Profil pro
    Analyste / Programmeur / DBA
    Inscrit en
    Juillet 2006
    Messages
    3 932
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 43
    Localisation : Belgique

    Informations professionnelles :
    Activité : Analyste / Programmeur / DBA
    Secteur : Distribution

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 932
    Par défaut
    Ca faisait des années que je n'étais plus venu sur ce forum. Merci les gars, ça fait plaisir de voir des gars qui comprennent qqch au sql (les devs de nos jours... c'est assez triste).
    Pour revenir au sujet, j'ai l'impression que la proposition de Seb est la simplification que je cherche. Si on prend la ligne avec la date de fin la plus grande mais en limitant aux lignes ayant une date de début antérieure à la date du jour, on aura soit :
    • la ligne courante dans le cas où il existe une date de fin postérieure à la date du jour
    • la dernière ligne valide dans le cas où il n'existe pas de date de fin postérieure à la date du jour.

    Il ne me reste plus qu'à voir comment implémenter cela avec entity framework (pas le choix malheureusement). Je passe toujours par le sql en premier pour me faire une idée de ce vers quoi je dois aller.

    Encore merci à vous !!

  9. #9
    Membre Expert
    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Décembre 2019
    Messages
    1 185
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Nord (Nord Pas de Calais)

    Informations professionnelles :
    Activité : Développeur Oracle

    Informations forums :
    Inscription : Décembre 2019
    Messages : 1 185
    Par défaut
    Bonjour,

    Je pense que tu as juste besoin de conditionner la clause ORDER BY dans la fonction ROW_NUMBER:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    with CTE_ADR_TMP as
    (
    select ADR_ID, CLI_ID, ADR_START_DATE, ADR_END_DATE, ADR_LINE1, ADR_LINE2, ADR_LINE3, ADR_START_DATE, ADR_END_DATE
         , row_number() over(partition by CLI_ID order by case when getdate() between ADR_START_DATE and ADR_END_DATE then 0 else 1 end,  ADR_END_DATE desc, ADR_START_DATE desc) as rn
      from T_ADDRESS_ADR
    )
    select * from CTE_ADR_TMP 
    where rn = 1;

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

Discussions similaires

  1. [langage] recuperer le dernier mot dune ligne
    Par kacedda dans le forum Langage
    Réponses: 6
    Dernier message: 29/04/2003, 14h38
  2. Exécuter la classe courante ??
    Par sami_c dans le forum Eclipse Java
    Réponses: 1
    Dernier message: 17/03/2003, 17h47
  3. Trouver le dernier enregistrement d'une table
    Par remi59 dans le forum Requêtes
    Réponses: 4
    Dernier message: 11/03/2003, 14h54
  4. repertoire courant
    Par mobee dans le forum Autres éditeurs
    Réponses: 2
    Dernier message: 11/12/2002, 13h03
  5. [VB6] [Système] Tuer un processus courant
    Par mdoyer dans le forum VB 6 et antérieur
    Réponses: 7
    Dernier message: 22/10/2002, 14h47

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