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 :

Récursivité dans une table


Sujet :

Langage SQL

  1. #1
    Membre régulier
    Profil pro
    Inscrit en
    Août 2009
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 80
    Points : 88
    Points
    88
    Par défaut Récursivité dans une table
    Bonjour,

    Je cherche à traiter un cas que je trouve compliqué.
    J'ai en entrée :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    col1 col2
    AA BB
    BB CC
    CC DD
    EE FF
    GG HH
    HH ZZ
    En sortie, je souhaite obtenir ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    col1 col2
    AA DD
    EE FF
    GG ZZ
    En gros quand la valeur de la seconde colonne se retrouve sur une autre ligne et à la première colonne, je prends les deux extrémités.
    Avez-vous une idée svp ?

    Merci pour votre aide.

  2. #2
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    On peut probablement faire autrement, voici en SQL standard ce qu'on peut faire :
    Code sql : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    with hier (col1, col2, niv)
    as
    (
            -- On cherche les points de départ (col1 inexistant en col2)
    	select t1.col1, t1.col2, 1 from test t1 where not exists (select 1/0 from test t2 where t2.col2 = t1.col1)
    	union
            -- On complète la hiérarchie en incrémentant le niveau
    	select h.col1, t.col2, h.niv + 1 from test t inner join hier h on h.col2 = t.col1
    )
    -- Et on ne conserve que la valeur de col2 pour le plus grand niveau
    select distinct col1, first_value(col2) over (partition by col1 order by niv desc) from hier;
    Testé avec SQLite, marchera aussi avec SQL Server.
    On ne jouit bien que de ce qu’on partage.

  3. #3
    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
    union all entre la racine et la récursion - je suis même étonné que ça fonctionne avec union.

  4. #4
    Membre régulier
    Profil pro
    Inscrit en
    Août 2009
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 80
    Points : 88
    Points
    88
    Par défaut
    Bonsoir et merci pour vos réponses.

    Dans le
    Je vois une division par zéro

  5. #5
    Membre chevronné
    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Décembre 2019
    Messages
    1 138
    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 138
    Points : 1 918
    Points
    1 918
    Par défaut
    Bonjour,

    Ce n'est pas de la récursivité. Ce que tu cherches à faire s'appelle "start-of-group" en anglais, je ne sais pas s'il existe un nom en français. Voici la méthode à utiliser (j'ai simuler ta table par le bloc "t" ci-dessous):

    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
    with t(col1, col2) 
    as (select 'AA', 'BB' from dual union all 
    select 'BB', 'CC' from dual union all 
    select 'CC', 'DD' from dual union all 
    select 'EE', 'FF' from dual union all 
    select 'GG', 'HH' from dual union all 
    select 'HH', 'ZZ' from dual 
    ), 
    ruptures as ( 
    select col1, 
           col2, 
            case when lag(col2, 1, 0) over (order by col1) <> col1 then 1 end flag_rupt
    from t), 
    groupes as ( 
    select col1, 
           col2, 
           flag_rupt, 
           sum(flag_rupt) over (order by col1) num_groupe 
    from ruptures) 
    select min(col1), max(col2) 
    from groupes  
    group by num_groupe;
     
     
    MIN(COL1)	MAX(COL2)
    AA	DD
    EE	FF
    GG	ZZ

  6. #6
    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
    Je pense que ça dépend, ici les données sont triées mais si ce n'est pas le cas la récursivité est une solution.

    Si au lieu de :
    GG HH
    HH ZZ
    Il y a :
    GG NN
    NN HH
    HH ZZ
    La solution récursive fonctionne et mais pas la "start-of-group".

  7. #7
    Membre régulier
    Profil pro
    Inscrit en
    Août 2009
    Messages
    80
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2009
    Messages : 80
    Points : 88
    Points
    88
    Par défaut
    Effectivement, dans la réalité, les données ne sont pas triées et le trie qu'on voit dans l'exemple cité est une pure coïncidence.
    Merci pour vos réponses. Je vais tester ça et je reviens vers vous.

  8. #8
    Membre chevronné
    Homme Profil pro
    Développeur Oracle
    Inscrit en
    Décembre 2019
    Messages
    1 138
    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 138
    Points : 1 918
    Points
    1 918
    Par défaut
    Oui, j'ai oublié de préciser qu'il fallait une colonne pour le tri, j'ai choisi col1 mais il faudrait connaitre la vraie structure des données.

  9. #9
    Modérateur
    Avatar de escartefigue
    Homme Profil pro
    bourreau
    Inscrit en
    Mars 2010
    Messages
    10 136
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Loir et Cher (Centre)

    Informations professionnelles :
    Activité : bourreau
    Secteur : Finance

    Informations forums :
    Inscription : Mars 2010
    Messages : 10 136
    Points : 38 561
    Points
    38 561
    Billets dans le blog
    9
    Par défaut
    Citation Envoyé par emmachane Voir le message
    Je vois une division par zéro
    Bonsoir

    Aucune importance : (NOT)EXISTS ne transporte aucune colonne mais seulement un booléen en fonction du succès ou non de la requête.
    Du coup
    SELECT 1.
    SELECT 'martine à la plage'.
    SELECT *.
    ou encore
    SELECT 1/0.

    renvoient le même résultat

    Sauf que...
    SELECT 1/0 peut produire des warning avec certains SGBD (c'est le cas pour DB2 for Z/OS), warnings pas forcément appréciés de l'exploitation qui veille au grain du coup

    à noter également
    SELECT NULL n'est pas accepté par tous les SGBD

  10. #10
    Expert éminent
    Avatar de StringBuilder
    Homme Profil pro
    Chef de projets
    Inscrit en
    Février 2010
    Messages
    4 153
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Chef de projets
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Février 2010
    Messages : 4 153
    Points : 7 403
    Points
    7 403
    Billets dans le blog
    1
    Par défaut
    Citation Envoyé par Waldar Voir le message
    union all entre la racine et la récursion - je suis même étonné que ça fonctionne avec union.
    Oups, oui en effet.

    Pour SQLite ça lui fait ni chaud ni froid

    En effet, avec SQL Server ça produit une erreur (avec la correction explicitement indiquée dans le message d'erreur d'ailleurs).
    On ne jouit bien que de ce qu’on partage.

  11. #11
    Rédacteur

    Avatar de SQLpro
    Homme Profil pro
    Expert bases de données / SQL / MS SQL Server / Postgresql
    Inscrit en
    Mai 2002
    Messages
    21 768
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Expert bases de données / SQL / MS SQL Server / Postgresql
    Secteur : Conseil

    Informations forums :
    Inscription : Mai 2002
    Messages : 21 768
    Points : 52 577
    Points
    52 577
    Billets dans le blog
    5
    Par défaut
    Avec ce jeu d'essais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CREATE TABLE T_LISTE_CHAINEE_LCN
    (LCN_GAUCHE    VARCHAR(16) NOT NULL PRIMARY KEY,
     LCN_DROITE    VARCHAR(16) NOT NULL UNIQUE,
     CHECK (LCN_GAUCHE <> LCN_DROITE));
     GO
     
     INSERT INTO T_LISTE_CHAINEE_LCN VALUES
    ('AA', 'BB'),
    ('BB', 'CC'),
    ('CC', 'DD'),
    ('EE', 'FF'),
    ('GG', 'HH'),
    ('HH', 'ZZ');
    Les requêtes suivantes :

    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
    WITH liste 
    as
    (-- On cherche les points de départ (LCN_GAUCHE inexistant en LCN_DROITE)
    SELECT t1.LCN_GAUCHE, t1.LCN_DROITE, 1 AS niv
    FROM   T_LISTE_CHAINEE_LCN AS t1 
    WHERE  NOT EXISTS (SELECT 1 
                       FROM   T_LISTE_CHAINEE_LCN AS t2 
                       WHERE  t2.LCN_DROITE = t1.LCN_GAUCHE)
    UNION ALL
    -- On complète la hiérarchie en incrémentant le niveau
    SELECT h.LCN_GAUCHE, t.LCN_DROITE, h.niv + 1 
    FROM   T_LISTE_CHAINEE_LCN AS t 
           inner join liste AS h ON h.LCN_DROITE = t.LCN_GAUCHE
    )
    -- Et on ne conserve que la valeur de LCN_DROITE pour le plus grand niveau
    SELECT LCN_GAUCHE, FIRST_VALUE(LCN_DROITE) OVER (partition by LCN_GAUCHE order by niv desc) AS LCN_DROITE 
    from   liste;
    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
    WITH liste 
    as
    (-- On cherche les points de départ (LCN_GAUCHE inexistant en LCN_DROITE)
    SELECT t1.LCN_GAUCHE, t1.LCN_DROITE, 1 AS niv
    FROM   T_LISTE_CHAINEE_LCN AS t1 
    WHERE  NOT EXISTS (SELECT 1 
                       FROM   T_LISTE_CHAINEE_LCN AS t2 
                       WHERE  t2.LCN_DROITE = t1.LCN_GAUCHE)
    UNION ALL
    -- On complète la hiérarchie en incrémentant le niveau
    SELECT h.LCN_GAUCHE, t.LCN_DROITE, h.niv + 1 
    FROM   T_LISTE_CHAINEE_LCN AS t 
           inner join liste AS h ON h.LCN_DROITE = t.LCN_GAUCHE
    ),
    liste_rang AS
    (
    -- calcule de rang sur sur l'écart de niveau
    SELECT l1.*, RANK() OVER(PARTITION BY LCN_GAUCHE ORDER BY niv DESC) AS RANG
    FROM   liste AS l1
    ) 
    SELECT LCN_GAUCHE, LCN_DROITE
    FROM   liste_rang 
    WHERE  RANG = 1;

    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
    -- par recherche de rupture et calcul de groupes
    with t (col1, col2) 
    as (select * FROM T_LISTE_CHAINEE_LCN
    ), 
    ruptures as ( 
    select col1, 
           col2, 
            case when lag(col2, 1, 0) over (order by col1) <> col1 then 1 end flag_rupt
    from t), 
    groupes as ( 
    select col1, 
           col2, 
           flag_rupt, 
           sum(flag_rupt) over (order by col1) num_groupe 
    from ruptures) 
    select min(col1), max(col2) 
    from groupes  
    group by num_groupe;
    Montre que la solution par rupture et groupe est la plus optimisée (SQL Server).
    Alors que c'est l'inverse dans ce autre exemple :
    https://www.developpez.net/forums/d2.../#post11677829

    A +
    Frédéric Brouard - SQLpro - ARCHITECTE DE DONNÉES - expert SGBDR et langage SQL
    Le site sur les SGBD relationnels et le langage SQL: http://sqlpro.developpez.com/
    Blog SQL, SQL Server, SGBDR : http://blog.developpez.com/sqlpro
    Expert Microsoft SQL Server - M.V.P. (Most valuable Professional) MS Corp.
    Entreprise SQL SPOT : modélisation, conseils, audit, optimisation, formation...
    * * * * * Expertise SQL Server : http://mssqlserver.fr/ * * * * *

Discussions similaires

  1. Lire un attribut dans un fichier XML en C++
    Par ti.k-nar dans le forum XML
    Réponses: 2
    Dernier message: 14/10/2002, 15h22
  2. Balises HTML dans un fichier XML
    Par Bastet79 dans le forum XML/XSL et SOAP
    Réponses: 12
    Dernier message: 04/09/2002, 15h29
  3. Sauvegarder une surface dans un fichier
    Par Freakazoid dans le forum DirectX
    Réponses: 6
    Dernier message: 18/08/2002, 15h23
  4. séparateurs dans un fichier
    Par manuhard dans le forum Langage
    Réponses: 5
    Dernier message: 13/08/2002, 11h30
  5. enregistrer dans un fichier avec une appli mdi
    Par ferrari dans le forum C++Builder
    Réponses: 4
    Dernier message: 05/05/2002, 15h17

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