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

MS SQL Server Discussion :

Jointure Triangulaire comment l'éviter?


Sujet :

MS SQL Server

  1. #1
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    956
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 956
    Points : 1 199
    Points
    1 199
    Par défaut Jointure Triangulaire comment l'éviter?
    Bonjour,
    J'ai une table contenant un historique notamment 4 colonnes :
    CODE
    LIBELLE
    DATE_OUVERTURE
    DATE_FERMETURE

    On doit avoir le libellé d'un CODE donné pour une plage comprise entre DATE_OUVERTURE et DATE_FERMETURE.
    J'ai réussi à remplir cette table ainsi
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    CODE    libelle      DATE_OUVERTURE	DATE_FERMETURE	
    01      lib012007    01/01/2007           NULL
    01	 lib012007b   01/05/2007           NULL
    01      lib012008    01/01/2008           NULL
     
    02      Lib2 2007    01/01/2007           NULL
    02      Lib2 2007b   05/06/2007           NULL
     
    03      LIB3 2007    01/01/2007           NULL
     
    04      LIB3 2007    01/01/2007	         15/03/2008
    Et je voudrais trouver les dates de fermetures manquantes, qui sont la date d'ouverture immédiatement supérieure pour le même code.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    CODE    libelle      DATE_OUVERTURE	DATE_FERMETURE	
    01      lib012007    01/01/2007           01/05/2007
    01	 lib012007b   01/05/2007           01/01/2008
    01      lib012008    01/01/2008           NULL
     
    02      Lib2 2007    01/01/2007           05/06/2007
    02      Lib2 2007b   05/06/2007           NULL
     
    03      LIB3 2007    01/01/2007           NULL
     
    04      LIB3 2007    01/01/2007	         15/03/2008
    Pour l'instant voici ma requête, elle est triangulaire.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    Update PALIER
    SET DATE_FERMETURE=
                (SELECT MIN(F.DATE_OUVERTURE)
                from PALIER F
                where PALIER.CODE=F.CODE
                AND F.DATE_OUVERTURE>PALIER.DATE_OUVERTURE
                )
     
    where DATE_FERMETURE is null
    AND  EXISTS ( 	SELECT * from PALIER E 
    		WHERE E.CODE=PALIER.CODE 
    		AND E.DATE_OUVERTURE>PALIER.DATE_OUVERTURE
    	    );
    le EXISTS sert à ne pas mettre à jour les lignes qui ne changent pas dont la date de fermeture reste à NULL, il est utile car il y a des trigger sur update dans cette table.
    Avez vous une autre façon d'écrire cette requête qui soit plus élégante et sans jointure triangulaire?
    Ps : la requête doit fonctionner en SQL Server 2000 et 2005

    Merci de votre aide
    Soazig

  2. #2
    Modérateur

    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Janvier 2005
    Messages
    5 826
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 42
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2005
    Messages : 5 826
    Points : 12 371
    Points
    12 371
    Par défaut
    Bonjour,

    Essayez la requête suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    UPDATE PALIER
    SET DATE_FERMETURE = F.DATE_OUVERTURE
    FROM dbo.PALIER AS P
    JOIN (
    		SELECT CODE, MIN(DATE_OUVERTURE) AS MIN_DATE_OUVERTURE
    		FROM dbo.PALIER
    		GROUP BY CODE
    	) AS F (CODE, MIN_DATE_OUVERTURE)
    	ON P.CODE = F.CODE
    WHERE DATE_FERMETURE IS NULL
    Elle est peut-être incomplète au regard de la requête que vous donnez puisqu'il n'y a pas le prédicat EXISTS : j'ai enlevé celui-ci car on procède à une auto-jointure (coûteuse) dont je ne comprends pas le prédicat (AND E.DATE_OUVERTURE>P.DATE_OUVERTURE).

    @++

  3. #3
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    956
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 956
    Points : 1 199
    Points
    1 199
    Par défaut
    Bonjour,
    Je ne crois pas que ta requête donne le bon résultat.
    Pour moi elle donnera cela
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    CODE    libelle      DATE_OUVERTURE       DATE_FERMETURE	
    01      lib012007    01/01/2007           01/01/2007
    01      lib012007b   01/05/2007           01/01/2007
    01      lib012008    01/01/2008           01/01/2007
     
    02      Lib2 2007    01/01/2007           01/01/2007
    02      Lib2 2007b   05/06/2007           01/01/2007
     
    03      LIB3 2007    01/01/2007           01/01/2007
     
    04      LIB3 2007    01/01/2007	         15/03/2008
    C'est à dire que toutes les dates de fermeture d'un code sont les mêmes.
    Alors que la date de fermeture d'un code doit être la date d'ouverture du palier suivant pour le même code s'il y en a un.

    Tu as raison de ne pas comprendre E.DATE_OUVERTURE>P.DATE_OUVERTURE
    car il fallait lire E.DATE_OUVERTURE>PALIER.DATE_OUVERTURE dans le exists.
    Je vais éditer le premier message pour corriger cette coquille.
    A+
    Soazig

  4. #4
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Customer Success Manager @Vertica
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    En créant un ID temporaire par code.
    Voici le jeu de test :
    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
    DECLARE @PALIER AS TABLE
    (
      CODE    VARCHAR(2),
      LIBELLE VARCHAR(10),
      DT_OUVR SMALLDATETIME,
      DT_FERM SMALLDATETIME
    );
     
    INSERT INTO @PALIER
    SELECT '01', 'lib012007' , CAST('01/01/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '01', 'lib012007b', CAST('01/05/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '01', 'lib012008' , CAST('01/01/2008' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '02', 'Lib2 2007' , CAST('01/01/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '02', 'Lib2 2007b', CAST('05/06/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '03', 'LIB3 2007' , CAST('01/01/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '04', 'LIB3 2007' , CAST('01/01/2007' AS SMALLDATETIME), CAST('03/15/2008' AS SMALLDATETIME)
    ;
     
    SELECT * FROM @PALIER;
     
    CODE LIBELLE    DT_OUVR                 DT_FERM
    ---- ---------- ----------------------- -----------------------
    01   lib012007  2007-01-01 00:00:00     NULL
    01   lib012007b 2007-01-05 00:00:00     NULL
    01   lib012008  2008-01-01 00:00:00     NULL
    02   Lib2 2007  2007-01-01 00:00:00     NULL
    02   Lib2 2007b 2007-05-06 00:00:00     NULL
    03   LIB3 2007  2007-01-01 00:00:00     NULL
    04   LIB3 2007  2007-01-01 00:00:00     2008-03-15 00:00:00
    Et la résolution avec une fonction analytique (je ne suis pas sûr pour SQL Server 2000 par contre) :
    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
    WITH PALIER (CODE, LIBELLE, DT_OUVR, DT_FERM, RN) AS
    (
    SELECT CODE,LIBELLE, DT_OUVR, DT_FERM,
           ROW_NUMBER() OVER(PARTITION BY CODE ORDER BY DT_OUVR ASC)
    FROM @PALIER
    )
    UPDATE P1
    SET P1.DT_FERM = P2.DT_OUVR
    FROM
        PALIER AS P1
        INNER JOIN PALIER AS P2
          ON P2.CODE = P1.CODE
         AND P2.RN = P1.RN + 1
    WHERE P1.DT_FERM IS NULL;
     
    SELECT * FROM @PALIER;
     
    CODE LIBELLE    DT_OUVR                 DT_FERM
    ---- ---------- ----------------------- -----------------------
    01   lib012007  2007-01-01 00:00:00     2007-01-05 00:00:00
    01   lib012007b 2007-01-05 00:00:00     2008-01-01 00:00:00
    01   lib012008  2008-01-01 00:00:00     NULL
    02   Lib2 2007  2007-01-01 00:00:00     2007-05-06 00:00:00
    02   Lib2 2007b 2007-05-06 00:00:00     NULL
    03   LIB3 2007  2007-01-01 00:00:00     NULL
    04   LIB3 2007  2007-01-01 00:00:00     2008-03-15 00:00:00

  5. #5
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    956
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 956
    Points : 1 199
    Points
    1 199
    Par défaut
    Bonjour,
    waldar, je confirme que sql server 2000 ne connait ni With, ni row_number() ni over.
    Serveur*: Msg 156, Niveau 15, État 1, Ligne 1
    Incorrect syntax near the keyword 'WITH'.
    Serveur*: Msg 195, Niveau 15, État 1, Ligne 4
    'ROW_NUMBER' is not a recognized function name.
    Merci de ton aide
    Soazig

  6. #6
    Expert éminent sénior
    Avatar de mikedavem
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2005
    Messages
    5 450
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Distribution

    Informations forums :
    Inscription : Août 2005
    Messages : 5 450
    Points : 12 891
    Points
    12 891
    Par défaut
    Bonjour,

    Une requête qui devrait fonctionner sur 2000.
    Il faudra l'adapter pour votre cas mais en principe elle devrait fonctionner. Elle part du même principe que celle proposée par Waldar. Pas sûr qu'elle soit plus optimisée...

    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
     
    DECLARE @PALIER AS TABLE
    (
      CODE    VARCHAR(2),
      LIBELLE VARCHAR(10),
      DT_OUVR SMALLDATETIME,
      DT_FERM SMALLDATETIME
    );
     
    INSERT INTO @PALIER
    SELECT '01', 'lib012007' , CAST('01/01/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '01', 'lib012007b', CAST('01/05/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '01', 'lib012008' , CAST('01/01/2008' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '02', 'Lib2 2007' , CAST('01/01/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '02', 'Lib2 2007b', CAST('05/06/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '03', 'LIB3 2007' , CAST('01/01/2007' AS SMALLDATETIME), CAST(NULL         AS SMALLDATETIME) UNION ALL
    SELECT '04', 'LIB3 2007' , CAST('01/01/2007' AS SMALLDATETIME), CAST('15/03/2008' AS SMALLDATETIME)
    ;
     
    UPDATE  @PALIER 
    SET DT_FERM = p6.DT_OUVR
    FROM @PALIER p0
    INNER JOIN 
    (
      SELECT p.CODE, P.LIBELLE, P5.DT_OUVR
      FROM
      (
        SELECT 
           (SELECT COUNT(*) FROM @PALIER p2
             WHERE p2.CODE <= p1.CODE AND p2.LIBELLE <= p1.LIBELLE) AS number,
           p1.*
        FROM @PALIER p1
      ) p
      INNER JOIN (SELECT 
    	         (SELECT COUNT(*) 
    	           FROM @PALIER p4
    	           WHERE p4.CODE <= p3.CODE AND p4.LIBELLE <= p3.LIBELLE) AS number,
    	      p3.*
    	      FROM @PALIER p3) p5
      ON p.number = p5.number - 1 
       AND p.CODE = p5.CODE
    ) p6
    ON p0.CODE = p6.CODE
     AND p0.LIBELLE = p6.LIBELLE
     
    SELECT * FROM @PALIER;
    ++

  7. #7
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    956
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 956
    Points : 1 199
    Points
    1 199
    Par défaut
    Bonjour,
    Je ne vois pas ce que viennent faire les libellés dans la requête.
    J'ai mis les libellés dans l'exemple pour qu'on comprenne qu'il y a d'autres informations qui changent selon les paliers.
    Les différents libellés qui changent pourrait être "obstétrique" qui devient "obstétrique et maternité" qui devient "gynécologie"
    Et dans la réalité j'ai d'autres colonnes qui changent d'un palier à l'autre.
    Ma requête ne prenait pas en compte la colonne LIBELLE.

    Cordialement
    Soazig

  8. #8
    Expert éminent sénior
    Avatar de mikedavem
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2005
    Messages
    5 450
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Distribution

    Informations forums :
    Inscription : Août 2005
    Messages : 5 450
    Points : 12 891
    Points
    12 891
    Par défaut
    Comme je vous l'ai dit il faut adapter à votre besoin .

    L'idée ici est de simuler le ROW_NUMBER() non disponible sur SQL Server 2000.

    ++

  9. #9
    Modérateur
    Avatar de Waldar
    Homme Profil pro
    Customer Success Manager @Vertica
    Inscrit en
    Septembre 2008
    Messages
    8 452
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Val de Marne (Île de France)

    Informations professionnelles :
    Activité : Customer Success Manager @Vertica
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2008
    Messages : 8 452
    Points : 17 820
    Points
    17 820
    Par défaut
    Citation Envoyé par mikedavem Voir le message
    L'idée ici est de simuler le ROW_NUMBER() non disponible sur SQL Server 2000.
    Je pense que c'est impossible à réaliser sans effectuer de jointure triangulaire, donc au final ça ne sera pas meilleur que la requête initiale.

  10. #10
    Expert éminent sénior
    Avatar de mikedavem
    Homme Profil pro
    Administrateur de base de données
    Inscrit en
    Août 2005
    Messages
    5 450
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Administrateur de base de données
    Secteur : Distribution

    Informations forums :
    Inscription : Août 2005
    Messages : 5 450
    Points : 12 891
    Points
    12 891
    Par défaut
    Citation Envoyé par Waldar Voir le message
    Je pense que c'est impossible à réaliser sans effectuer de jointure triangulaire, donc au final ça ne sera pas meilleur que la requête initiale.
    Tu as raison Waldar... je l'ai bien précisé dans mon précédent post ... je ne pense pas que ce soit plus optimisé. Après lecture des plans d'exécution et IO générés ... ce n'est sûrement pas la meilleur solution

    ++

  11. #11
    Membre éprouvé
    Profil pro
    Inscrit en
    Octobre 2002
    Messages
    956
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2002
    Messages : 956
    Points : 1 199
    Points
    1 199
    Par défaut
    Bonjour,
    Eh bien merci de votre aide, je vais donc garder ma requête initiale puis qu'elle semble pas si mauvaise que cela pour SQL Server 2000.

    Merci à tous
    Soazig

Discussions similaires

  1. 2 variables qui pointent vers les mêmes valeurs: comment l'éviter?
    Par skystef dans le forum Débuter avec Java
    Réponses: 4
    Dernier message: 03/04/2008, 11h51
  2. Logiciel de filtre web. Comment les éviter
    Par babemagus dans le forum Applications
    Réponses: 3
    Dernier message: 03/03/2006, 17h38
  3. Problemes de doublons...comment les éviter?
    Par paflolo dans le forum Langage SQL
    Réponses: 5
    Dernier message: 28/02/2006, 14h57
  4. Réponses: 8
    Dernier message: 16/06/2005, 13h58
  5. Fonction template virtuelle... comment l'éviter ?
    Par :Bronsky: dans le forum Langage
    Réponses: 12
    Dernier message: 07/06/2005, 14h21

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