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 :

Représentation d'arbres entremêlés


Sujet :

Langage SQL

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 10
    Points : 5
    Points
    5
    Par défaut Représentation d'arbres entremêlés
    Bonjour,

    La lecture de l'article http://sqlpro.developpez.com/cours/arborescence/ de SQLPro sur la gestion intervallaire des arbres m'a donné des pistes de recherche sur un problème que je dois gérer. Mais mon problème est un peu plus complexe : il s'agit de la gestion de chaînes d'actionnariats entre entreprises.

    Les actionnaires d'une entreprise (formellement l'entité actionaire est identique à l'entité entreprise) peuvent être représentés par un arbre : les actionnaires directs, puis les actionnaires des actionnaires, ... La méthode décrite dans l'article convient parfaitement

    Mais deux entreprises peuvent avoir des actionnaires communs. C'est-à-dire que deux arbres peuvent entremêler leurs noeuds et leurs feuilles (un actionnaire "noeud" pour l'un pouvant d'ailleurs être une feuille pour l'autre).

    Les trois questions que les utilisateurs poseront sont essentiellement :
    A) quels sont les actionnaires d'une entreprise ? et en particulier les actionnaires "teminaux" (les feuilles) ?
    B) quelles sont les entreprises "filiales" d'un actionnaire ?
    C) est-ce que deux entreprises sont cousines (c'est-à-dire qu'elles ont des parents communs).

    Plus des questions complémentaires, mais que je laisse de côté : les pourcentages de participation et des "clichés" annuels.

    Je laisse également de côté les actionnariats circulaires : A actionnaire de B actionnaire de C actionnaire de A. La base actuelle est composée de 2000 entreprises et ne comporte aucune référence circulaire. Cela ne veut pas dire qu'il n'y en aura pas, mais en raison d'un principe de pragmatisme, on se débrouillera pour gérer LE cas si jamais il arrive (quitte à dupliquer exceptionnellement une entreprise).

    J'ai deux options à ma disposition :
    - soit faire des structures parallèles d'arbres (ce qui permet de répondre facilement à la question A) avec des noeuds et des feuilles entremêlées.
    - soit considérer les filiales d'une entreprise comme un arbre à l'envers (ce qui permet de répondre facilement à la question B). Visuellement, faire partir un arbre à l'envers à partir d'un noeud ou d'une feuille.

    Mais dans les deux cas, l'obligation de parcourir plusieurs arbres pour la question C.

    Sachant que dans les deux cas, le principal souci, ce sont les modifications : un actionnaire cède toutes ses parts à un autre, un nouvel actionnaire apparaît, .... D'où la nécessité de modifier simultanément plusieurs arbres. Et nous savons tous que la redondance d'informations, ce n'est jamais bon !!!

    J'ai également regardé du côté des structures généalogiques, mais sans succès : elles sont prévues pour que chaque personne ait un seul père et une seule mère. Or là, une entreprise peut avoir un paquet de parents.

    D'où mes questions :
    - Est-ce que quelqu'un a une solution toute faite (un article, une structure de base, ...) ? ça serait la réponse idéale
    - Ou alors, est-ce que quelqu'un aurait des idées ou des pistes de solution ?

    Merci de vos réponses

  2. #2
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut Requêtes récursives
    Bonsoir,


    Je ne suis pas sûr d’avoir capté exactement votre besoin, mais on va faire comme si...

    Je me situe dans un contexte SQL Server. Créons une table des entreprises ainsi qu’une table des liens qui unissent ces entreprises :

    Code SQL : 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
     
    CREATE TABLE ENTREPRISE
    (
            EntId    Char(6)      NOT NULL
          , EntNom   Varchar(32)  NOT NULL
        , CONSTRAINT ENTREPRISE_PK PRIMARY KEY (EntId)
    ) ;
    CREATE TABLE ACTIONNAIRE
    (
            EnfantId    Char(6)      NOT NULL
          , PaRentId    Char(6)      NOT NULL
        , CONSTRAINT ACTIONNAIRE_PK PRIMARY KEY (EnfantId, ParentId)
        , CONSTRAINT ACTIONNAIRE_FK1 FOREIGN KEY (EnfantId) 
                 REFERENCES ENTREPRISE (EntId)
        , CONSTRAINT ACTIONNAIRE_FK2 FOREIGN KEY (ParentId) 
                 REFERENCES ENTREPRISE (EntId)
    ) ;

    Insérons quelques entreprises :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    INSERT INTO ENTREPRISE VALUES ('e01', 'entreprise 01') ;
    INSERT INTO ENTREPRISE VALUES ('e02', 'entreprise 02') ;
    INSERT INTO ENTREPRISE VALUES ('e03', 'entreprise 03') ;
    INSERT INTO ENTREPRISE VALUES ('e04', 'entreprise 04') ;
    INSERT INTO ENTREPRISE VALUES ('e05', 'entreprise 05') ;
    INSERT INTO ENTREPRISE VALUES ('e06', 'entreprise 06') ;
    INSERT INTO ENTREPRISE VALUES ('e07', 'entreprise 07') ;
    INSERT INTO ENTREPRISE VALUES ('e08', 'entreprise 08') ;
    INSERT INTO ENTREPRISE VALUES ('e09', 'entreprise 09') ;
    INSERT INTO ENTREPRISE VALUES ('e10', 'entreprise 10') ;
    INSERT INTO ENTREPRISE VALUES ('e11', 'entreprise 11') ;
    INSERT INTO ENTREPRISE VALUES ('e12', 'entreprise 12') ;
    INSERT INTO ENTREPRISE VALUES ('e13', 'entreprise 13') ;
    INSERT INTO ENTREPRISE VALUES ('e14', 'entreprise 14') ;
    INSERT INTO ENTREPRISE VALUES ('e15', 'entreprise 15') ;

    Insérons quelques liens :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    INSERT INTO ACTIONNAIRE VALUES ('e03', 'e01') ;
    INSERT INTO ACTIONNAIRE VALUES ('e03', 'e02') ;
    INSERT INTO ACTIONNAIRE VALUES ('e04', 'e01') ;
    INSERT INTO ACTIONNAIRE VALUES ('e05', 'e03') ;
    INSERT INTO ACTIONNAIRE VALUES ('e06', 'e03') ;
    INSERT INTO ACTIONNAIRE VALUES ('e06', 'e04') ;
    INSERT INTO ACTIONNAIRE VALUES ('e10', 'e06') ;
    INSERT INTO ACTIONNAIRE VALUES ('e11', 'e01') ;

    e03 est enfant de e01 et de e02, e06 est enfant de e03 et e04, etc.

    Pour récupérer la descendance d’une entreprise, e01 par exemple, vous pouvez utiliser une requête récursive :

    Code SQL : 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 DESCENDANCE (ParentId, EnfantId) AS
    (
     (
      SELECT   ParentId, EnfantId
      FROM     ACTIONNAIRE
      WHERE    ParentId = 'e01'
     )
    UNION ALL
     (SELECT   x.ParentId, x.EnfantId
      FROM     ACTIONNAIRE AS x JOIN DESCENDANCE AS y
                 ON y.EnfantId = x.ParentId
     )
    )
    SELECT DISTINCT z.EntNom AS Parent, y.EntNom AS Enfant 
    FROM   DESCENDANCE x 
             JOIN ENTREPRISE AS y ON x.EnfantId = y.EntId
             JOIN ENTREPRISE AS z ON x.ParentId = z.EntId ;


    Pour récupérer seulement les feuilles, vous remplacez le SELECT final par le suivant :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    SELECT DISTINCT '', y.EntNom AS Enfant 
    FROM   DESCENDANCE AS x 
             JOIN ENTREPRISE AS y ON x.EnfantId = y.EntId
             JOIN ENTREPRISE AS z ON x.ParentId = z.EntId
    WHERE   NOT EXISTS
          (SELECT ''
           FROM   DESCENDANCE AS t
           WHERE  x.EnfantId = t.ParentId)   
    ;


    Pour récupérer l’ascendance d’une entreprise, par exemple l’ascendance de e06 :

    Code SQL : 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
    WITH ASCENDANCE (EnfantId, ParentId) AS
    (
     (
      SELECT   EnfantId, ParentId
      FROM     ACTIONNAIRE
      WHERE    EnfantId = 'e06'
     )
    UNION ALL
     (SELECT   x.EnfantId, x.ParentId
      FROM     ACTIONNAIRE AS x JOIN ASCENDANCE AS y
                 ON x.EnfantId = y.ParentId
     )
    )
    SELECT DISTINCT y.EntNom AS Enfant, z.EntNom AS Parent 
    FROM   ASCENDANCE x 
             JOIN ENTREPRISE y ON x.EnfantId = y.EntId
             JOIN ENTREPRISE z ON x.ParentId = z.EntId 
    ;

    Je ne sais pas si j'ai répondu précisément à votre besoin, mais j'espère vous avoir donné des pistes. Pour la suite, je vous laisse œuvrer et passe le relais aux cracks de SQL.
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
    => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

    __________________________________
    Bases de données relationnelles et normalisation : de la première à la sixième forme normale
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 10
    Points : 5
    Points
    5
    Par défaut
    Merci pour votre réponse.

    Les requêtes récursives est une solution à laquelle j'avais pensé (ma base est sous Oracle, mais le concept y existe également), voire même un traitement récursif en dehors de SQL.

    Ce qui me plaisait dans la gestion intervallaire exposée dans l'article, c'est que justement on a un système extrêmement simple pour parcourir un arbre avec une seule requête.

    Au prix, il est vrai d'une complexité accrue des mises à jour : mais comme souvent, il y a plus souvent des lectures que des écritures et c'est cela que je veux privilégier.

    En tous cas, merci de vous être penché sur mon problème

  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
    Les systèmes intervallaires ont été surtout créé pour implémenter une pseudo-hierarchies sur les SGBD ne sachant pas les gérer.

    Le confort que vous gagnez en lecture vous le perdez en écriture.
    Utilisez le principe des id_parent / id_enfant.

  5. #5
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 10
    Points : 5
    Points
    5
    Par défaut
    J'entends bien votre réponse. On dérive un peu sur un débat plus théologique que technique. Bien entendu qu'une gestion "simple" (actionnaire, filiale) est possible. C'est d'ailleurs celle-ci qui est actuellement mise en oeuvre. Mais elle montre très vite ses limites et chaque nouvelle demande de notre client impose des développements fastidieux, parfois complexes ou impossibles à mettre en oeuvre à cause des temps de réponse.

    C'est pour cela que j'essaye de trouver des solutions un peu plus rusées que la méthode "bourrin" que le 1er stagiaire venu peut nous apporter. La gestion intervallaire des arbres me semble une bonne idée. Peu importe si c'est compliqué à mettre en oeuvre : c'est mon boulot et celui de mon équipe de développement. Nous sommes des professionnels, nous sommes payés pour ça.
    Mais nous sommes surtout dans une optique user-centric : Tout ce qui peut apporté un confort aux utilisateurs, que ce soit en termes de vitesses ou de fonctionnalités doit absolument être utilisé. Même si cela doit faire ch... les développeurs.

    Voila donc pourquoi je lance cette question. La gestion d'un arbre simple, c'est OK. Nous allons la mettre en oeuvre. Mais je repose la question : y a t'il une solution (ou des idées) pour la gestion d'arbres entremêlés ?

  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
    Hors sujet, vous vous contredisez :
    Citation Envoyé par antonomas Voir le message
    Mais elle montre très vite ses limites et chaque nouvelle demande de notre client impose des développements fastidieux, parfois complexes ou impossibles à mettre en oeuvre à cause des temps de réponse.
    Citation Envoyé par antonomas Voir le message
    Peu importe si c'est compliqué à mettre en oeuvre : c'est mon boulot et celui de mon équipe de développement. Nous sommes des professionnels, nous sommes payés pour ça.
    Pour répondre à votre question, je ne connais pas personnellement d'autres méthodes pour gérer les hierarchies.

    J'ai toujours utilisé les relations parents / enfants, je n'ai pas rencontré de problème particulier une fois la syntaxe assimilée.

    Maintenant, ça dépend toujours du contexte, des volumétries, et cetera : ce qui est vrai pour moi peut être faux pour vous (ça c'est de la réponse de consultant ).

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mai 2010
    Messages
    10
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 10
    Points : 5
    Points
    5
    Par défaut
    Waldar,

    Je suis pas là pour créer des polémiques, mais pour demander à des experts s'ils ont une réponse ou pas à mon problème.

    Vous trouvez que je me contredis : c'est peut-être vrai. Mais c'est probablement dû à un problème d'expression de ma part (ou à une mauvaise interprétation de la vôtre). Quand je dis que mes développeurs sont des professionnels et que peu m'importe le temps qu'ils passent, celà n'empêche pas que je préfère qu'ils prennent du temps pour plancher sur une solution difficile mais efficace que de prendre la même durée pour une solution simpliste, mais bancale.

    Je ne doute pas des solutions que vous avez mises en place, mais permettez-moi de demander aux experts s'ils ont des solutions efficaces au problème que je leur soumets. Il y aura peut-être des réponses positives ou peut-être pas : mais, de nouveau, je préfère avoir pris quelques minutes à poser la question plutôt que de perdre des heures à mettre en place un truc basique qui ne répondrait que partiellement au problème alors que le même temps pourrait être utilisé pour une solution efficace.

    Encore une fois, j'entends bien votre réponse sur la solution basique. Mais permettez-moi d'insister en demandant aux autres lecteurs de ce forum s'ils ont entendu parler d'une solution un peu plus efficace.

  8. #8
    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
    Ne vous méprenez pas sur mes propos, votre démarche est la bonne.

    Vous demandez des retours, vous avez eu le mien et je vous accorde bien volontiers que ce n'est qu'un retour dans une situation qui n'est pas la votre !

  9. #9
    Expert éminent sénior
    Avatar de fsmrel
    Homme Profil pro
    Spécialiste en bases de données
    Inscrit en
    Septembre 2006
    Messages
    8 002
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Spécialiste en bases de données
    Secteur : Conseil

    Informations forums :
    Inscription : Septembre 2006
    Messages : 8 002
    Points : 30 905
    Points
    30 905
    Billets dans le blog
    16
    Par défaut
    Bonsoir antonomas,


    Personne ne répondant à vos interrogations, je m’y colle à nouveau, histoire de faire avancer le Schimilimili.


    Citation Envoyé par antonomas
    Ce qui me plaisait dans la gestion intervallaire exposée dans l'article, c'est que justement on a un système extrêmement simple pour parcourir un arbre avec une seule requête.
    Je vous ferai observer qu’avec la solution que je vous avais proposée, il n’y a aussi qu’une seule requête. Par référence à l’article d’un des deux pères de SQL, Don Chamberlin, article paru en mai 1996 dans Database Programming and Design⁽¹⁾, on utilise un montage curieux, une Common Table Expression (CTE) dont l’objet est de calculer une vue temporaire (DESCENDANCE dans mon message précédent), une CTE étant définie comme un UNION ALL entre deux parties, la 1re étant la sous-requête initiale permettant d’amorcer la pompe, la 2e partie étant la sous-requête récursive, peuplant la vue temporaire. Une fois le traitement récursif terminé, le SELECT proprement dit produit le résultat du calcul.

    Exception faite des requêtes utilisant l’opérateur TCLOSE du Modèle Relationnel de Données, quoi de plus simple ? Tout un programme en une requête d’une dizaine de lignes (contre une ligne il est vrai avec TCLOSE...)


    Citation Envoyé par antonomas
    Bien entendu qu'une gestion "simple" (actionnaire, filiale) est possible. C'est d'ailleurs celle-ci qui est actuellement mise en oeuvre. Mais elle montre très vite ses limites et chaque nouvelle demande de notre client impose des développements fastidieux, parfois complexes ou impossibles à mettre en oeuvre à cause des temps de réponse.
    Aux réserves près que j’ai faites, à quelles limites faites-vous allusion ? Quelles sont les limites de l’utilisation des CTE dans le contexte de vote application ? Pourriez-vous donner un exemple concret excédant leurs possibilités ?

    Concernant les temps de réponse, si les index sont en place, les résultats des EXPLAIN conformes à ce que l’on attend, on devrait s’en sortir. Il ne m’arrive pas souvent d’avoir besoin d’utiliser des requêtes récursives, m’ai j’ai attaqué des nomenclatures de 500 000 lignes, par exemple pour rechercher un nœud et ses ascendants avec des temps de réponse de quelques dizaines de millisecondes. Évidemment, les index étaient en place et les résultats des EXPLAIN satisfaisants (NESTED LOOP, pas de TABLE SCAN).

    Un exemple d’EXPLAIN (Oracle) que j’ai utilisé il y a quelques années :



    Il est évident que, dans le cas d’Oracle, si on n’encapsule pas la partie Start With ... Connect By alors qu’il y a une jointure en jeu (entre les tables MY_ENTITE et NMCL_NOEUD dans cet exemple), la performance ne sera pas au rendez-vous. Mais après 40 années à crapahuter dans des tas de bases de données de tout poil, parfois gigantesques (1500 tables pour une base données, certaines tables contenant de l’ordre de 500 000 000 de lignes), si au départ les modèles conceptuels sont sains et robustes (normalisés), si l’on a effectué des séances poussées de prototypage dans les règles de l’art, j’ai constaté que les problèmes de performance sont toujours solubles (sauf si le langage proposé n’est pas algébriquement complet).

    Votre MCD recèle-t-il une structure telle que l’on soit dans l’incapacité de l’exploiter au moyen de ces CTE ? Est-il inapproprié pour les besoins de l’application ?

    Pour l’anecdote. C’est dans les vieux pots qu’on fait les meilleures soupes :

    Vu les quelques éléments que vous avez fournis, j’en suis resté à la structure que l’on utilise depuis les années soixante (avec DBOMP à l’époque pour les célèbres Bills of materials) et reprise par Ted Codd (père de la théorie relationnelle) dans son article fondateur de 1970, et qu’il manipule au moyen de l’algèbre relationnelle.

    Extrait de l’article de Codd (cela fait déjà 40 ans...) :



    Cette structure fort simple et on ne peut plus classique est celle qui me sert pour l’exemple que je vous ai proposé initialement et qui convient parfaitement à Waldar :



    Pour en revenir à votre MCD, s’il diffère sensiblement de celui-ci, pourriez-vous le présenter ? Rien de tel qu’une représentation graphique pour aborder un problème et éviter les quiproquos.


    Maintenant, je reprends l’exemple que je vous avais proposé en l’aménageant un peu. Je conserve en gros la structure de la table ACTIONNAIRE, dotée de deux colonnes, Parent et Enfant et correspondant aux diagrammes ci-dessus :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    CREATE TABLE ACTIONNAIRE
    (
             Parent    Char(6)      NOT NULL
          ,  Enfant    Char(6)      NOT NULL
        , CONSTRAINT ACTIONNAIRE_PK PRIMARY KEY (Parent, Enfant)
    ) ;

    Les relations Parent/Enfant sont les suivantes, façon Waldar, Codd et al. :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    INSERT INTO ACTIONNAIRE VALUES ('e1', 'e2') ;
    INSERT INTO ACTIONNAIRE VALUES ('e1', 'e3') ;
    INSERT INTO ACTIONNAIRE VALUES ('e1', 'e4') ;
    INSERT INTO ACTIONNAIRE VALUES ('e2', 'e5') ;
    INSERT INTO ACTIONNAIRE VALUES ('e2', 'e4') ;
    INSERT INTO ACTIONNAIRE VALUES ('e5', 'e7') ;
    INSERT INTO ACTIONNAIRE VALUES ('e4', 'e6') ;
    INSERT INTO ACTIONNAIRE VALUES ('e6', 'e9') ;
     
    INSERT INTO ACTIONNAIRE VALUES ('pa', 'pb') ;
    INSERT INTO ACTIONNAIRE VALUES ('pb', 'e4') ; -- connexion via e4 
    INSERT INTO ACTIONNAIRE VALUES ('pb', 'pc') ;
     
    INSERT INTO ACTIONNAIRE VALUES ('z1', 'z2') ; -- arbre indépendant 
    INSERT INTO ACTIONNAIRE VALUES ('z2', 'z3') ;

    On notera la connexion intéressante entre e4 et pb.

    Sous forme de graphe :




    Sous forme de forêt :



    Par référence à votre message initial, supposons que l’on veuille connaître :
    — La descendance d’un actionnaire donné,
    — Seulement les feuilles dans cette descendance,
    — L’ascendance d’un actionnaire donné,
    — Seulement les racines dans cette ascendance,
    — Les cousins d’un actionnaire donné (actionnaires ayant au moins un ancêtre commun).
    Le plus simple consiste à commencer par créer une vue permettant de calculer la fermeture transitive de la table ACTIONNAIRE. La fermeture transitive ACTIONNAIRE+ de ACTIONNAIRE est ainsi définie :

    La ligne <x,y> apparaît dans ACTIONNAIRE+ si et seulement si (a) elle apparaît dans ACTIONNAIRE, ou bien (b) il existe une valeur z telle que la ligne <x,z> apparaît dans ACTIONNAIRE et la ligne <z,y> apparaît dans ACTIONNAIRE+.

    D’après le graphe ci-dessus, la valeur de ACTIONNAIRE+ est la suivante :

    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
    Parent   Enfant
    ------   ------
      e1       e2    
      e1       e3    
      e1       e4    
      e1       e5    
      e1       e6    
      e1       e7    
      e1       e9    
      e2       e4    
      e2       e5    
      e2       e6    
      e2       e7    
      e2       e9    
      e4       e6    
      e4       e9    
      e5       e7    
      e6       e9    
      pa       e4    
      pa       e6    
      pa       e9    
      pa       pb    
      pa       pc    
      pb       e4    
      pb       e6    
      pb       e9    
      pb       pc    
      z1       z2    
      z1       z3    
      z2       z3
    Et au vu de la définition de la fermeture transitive, le code SQL de la vue permettant de calculer cette fermeture est le suivant :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    CREATE VIEW VFT (Parent, Enfant) AS 
       WITH FERMETURE (Parent, Enfant) AS
       ((
         SELECT   Parent, Enfant
         FROM     ACTIONNAIRE
        )
       UNION ALL
        (SELECT   ACTIONNAIRE.Parent, FERMETURE.Enfant
         FROM     ACTIONNAIRE  JOIN FERMETURE 
                     ON FERMETURE.Parent = ACTIONNAIRE.Enfant
       ))
       SELECT  * 
       FROM    FERMETURE ;

    A partir de là, on peut fournir les réponses aux questions qui viennent d'être énumérées :

    Requête 1

    Obtenir la descendance d’un actionnaire, par exemple celle de l’actionnaire e1. On utilise une restriction appliquée à la vue VFT :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT DISTINCT * 
    FROM   VFT 
    WHERE  Parent = 'e1'

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Parent  Enfant
    ------  ------
      e1      e2    
      e1      e3    
      e1      e4    
      e1      e5    
      e1      e6    
      e1      e7    
      e1      e9
    Requête 2

    A partir de e1, obtenir seulement les actionnaires feuilles :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SELECT DISTINCT * 
    FROM   VFT as x
    WHERE  Parent = 'e1'
      AND  NOT EXISTS 
          (SELECT  ''
           FROM    VFT AS y
           WHERE   x.Enfant = y.Parent) ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    Parent  Enfant
    ------  ------
      e1      e3    
      e1      e7    
      e1      e9
    Requête 3

    A partir de e1, obtenir seulement les actionnaires non feuilles (nœuds intermédiaires) :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SELECT DISTINCT * 
    FROM   VFT as x
    WHERE  Parent = 'e1'
      AND  EXISTS 
          (SELECT  ''
           FROM    VFT AS y
           WHERE   x.Enfant = y.Parent) ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    Parent  Enfant
    ------  ------
      e1      e2    
      e1      e4    
      e1      e5    
      e1      e6
    Requête 4

    Obtenir l’ascendance d’un actionnaire, par exemple l’ascendance de e6 :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    SELECT DISTINCT Enfant, Parent 
    from   VFT
    WHERE  Enfant = 'e6' ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    Enfant   Parent
    ------   ------
      e6       e1    
      e6       e2    
      e6       e4    
      e6       pa    
      e6       pb
    Vous aurez noté que les requêtes 1 et 4 sont semblables, à ceci près que la clause WHERE fait mention de l’attribut Parent dans le 1er cas, de l’attribut Enfant dans l’autre cas.

    Requête 5

    Obtenir seulement les actionnaires racines de l’actionnaire e6 :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
     
    SELECT DISTINCT Enfant, Parent AS Racine
    from   VFT AS x
    WHERE  Enfant = 'e6' 
     and NOT EXISTS
            (SELECT ''
             FROM   VFT AS y
             WHERE  x.Parent = y.Enfant) ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    Enfant    Racine
    ------   ------
      e6       e1    
      e6       pa
    Requête 6

    Calculer la fermeture transitive des actionnaires racines de l’actionnaire e6 :

    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    SELECT DISTINCT *
    FROM   VFT AS x
    WHERE  EXISTS
            (SELECT ''
             FROM   VFT AS y
             WHERE  y.Enfant = 'e6'
             AND    x.Parent = y.Parent 
             AND NOT EXISTS
                   (SELECT ''
                    FROM   VFT AS z
                    WHERE  y.Parent = z.Enfant)) ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Parent   Enfant
    ------   ------
      e1       e2    
      e1       e3    
      e1       e4    
      e1       e5    
      e1       e6    
      e1       e7    
      e1       e9    
      pa       e4    
      pa       e6    
      pa       e9    
      pa       pb    
      pa       pc
    Requête 7

    Obtenir les cousins de l’actionnaire e6 (e6 et ses cousins ont au moins un ancêtre commun) :

    Code SQL : 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
     
    01  SELECT DISTINCT Parent As Adam, Enfant as Cousin
    02  FROM   VFT AS x
    03  WHERE  EXISTS
    04          (SELECT ''
    05           FROM   VFT AS y
    06           WHERE  y.Enfant = 'e6'
    07           AND    x.Parent = y.Parent     
    08           AND NOT EXISTS
    09                 (SELECT ''
    10                  FROM   VFT AS z
    11                  WHERE  y.Parent = z.Enfant))
    12    AND  NOT EXISTS ------------- élimination des ascendants 
    13         (SELECT ''
    14          FROM   VFT AS y
    15          WHERE  y.Enfant = 'e6' 
    16          AND  x.Enfant = y.Parent)
    17    AND  NOT EXISTS ------------- élimination des descendants 
    18         (SELECT ''
    19          FROM   VFT AS y
    20          WHERE  y.Parent = 'e6' 
    21            AND  x.Enfant = y.Enfant)   
    22    AND  x.Enfant <> 'e6' ;

    Au résultat :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
     
    Adam   Cousin
    ----   ------
     e1      e3    
     e1      e5    
     e1      e7    
     pa      pc
    Cette requête mérite quelques explications pour les forumeurs de passage :

    Lignes 03-11 : calcul de la fermeture transitive des ancêtres racines de e6 (cf. requête 6).
    Lignes 12-16 : exclusion des ascendants de e6 (cf. requête 4).
    Lignes 17-21 : exclusion des descendants de e6 (cf. requête 1).
    Ligne 22 : exclusion de e6 lui-même.

    =>

    En français : mes cousins sont les descendants de mes ancêtres, à l’exclusion de moi-même, de mes ascendants et descendants.

    Conclusion

    Certes, la table ACTIONNAIRE façon Waldar, la vue VFT et les requêtes dont elle fait l’objet sont basiques et du niveau 1er stagiaire venu, mais elles peuvent constituer un bon socle pour des requêtes plus compliquées et des précurseurs comme Codd (RIP) ou Chamberlin les auront certainement utilisées à leur façon.


    ⁽¹⁾ Recursion in SQL: Tips and Techniques. Database Programming and Design, May 1996, pp. 47-52.
    Dans cet article, Don Chamberlin fait référence aux travaux de ceux qui ont conçu la syntaxe SQL des requêtes récursives, cf. Magic is relevant, The magic of duplicates and aggregates.
    (a) Faites simple, mais pas plus simple ! (A. Einstein)
    (b) Certes, E=mc², mais si on discute un peu, on peut l’avoir pour beaucoup moins cher... (G. Lacroix, « Les Euphorismes de Grégoire »)
    => La relativité n'existerait donc que relativement aux relativistes (Jean Eisenstaedt, « Einstein et la relativité générale »)

    __________________________________
    Bases de données relationnelles et normalisation : de la première à la sixième forme normale
    Modéliser les données avec MySQL Workbench
    Je ne réponds pas aux questions techniques par MP. Les forums sont là pour ça.

  10. #10
    Membre actif Avatar de aperrin
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    221
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 221
    Points : 272
    Points
    272
    Par défaut Représentation de la profondeur de l'arbre par les colonnes
    Bonjour,

    Que pensez vous de la représentation de la représentation d'un arbre en partant du postulat que la Profondeur de l'arbre est finie ?

    Un exemple avec un niveau fini de quatre colonnes
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Table
    ==================================
    Niveau 1     Niveau 2     Niveau 3          Niveau 3
    Transport   Marin         Voilier
    Transport   Marin         Paquebot
    Transport   Marin         Planche à voile
    Transport   Terrestre     Moto               Trail
    Transport   Terrestre     Moto               side-car
    Transport   Terrestre     camion
    Transport   Terrestre     voiture
    En essayant continuellement, on finit par réussir. Donc plus ça rate, plus on a de chances que ça marche !

  11. #11
    Membre actif Avatar de aperrin
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    221
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 221
    Points : 272
    Points
    272
    Par défaut
    Visiblement la représentation en colonne n'est pas préconisée :
    http://blog.webnaute.net/2006/05/23/..._arborescente/
    :
    Comme autre technique, il y en a une qui consiste à stocker carrément le chemin canonisé d’un élément à la racine. Pas super élégant et viable seulement pour des petits arbrisseaux ;¬)
    En essayant continuellement, on finit par réussir. Donc plus ça rate, plus on a de chances que ça marche !

  12. #12
    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
    Si la profondeur est fixe, un modèle relationnel classique fera l'affaire !

  13. #13
    Membre actif Avatar de aperrin
    Profil pro
    Inscrit en
    Octobre 2005
    Messages
    221
    Détails du profil
    Informations personnelles :
    Âge : 51
    Localisation : France

    Informations forums :
    Inscription : Octobre 2005
    Messages : 221
    Points : 272
    Points
    272
    Par défaut
    Citation Envoyé par Waldar Voir le message
    Si la profondeur est fixe, un modèle relationnel classique fera l'affaire !
    Qu'entends tu par une modele relationel classique ?
    Une table avec comme colonnes id et id_pere ?
    En essayant continuellement, on finit par réussir. Donc plus ça rate, plus on a de chances que ça marche !

  14. #14
    Futur Membre du Club
    Inscrit en
    Janvier 2011
    Messages
    1
    Détails du profil
    Informations forums :
    Inscription : Janvier 2011
    Messages : 1
    Points : 6
    Points
    6
    Par défaut arbre fini
    Il n'est pas conseillé de stocker l'arborescence en colonne car si tu changes par exemple le noeud racine tu dois réécrire dans chacune de tes lignes le nouveau noeud racine.

    Ce n'est pas stable.

    L'information est redondante.

Discussions similaires

  1. Représenter un arbre dans une console
    Par rambc dans le forum Général Python
    Réponses: 9
    Dernier message: 31/10/2010, 23h55
  2. [POO] Recursion sur un tableau représentant un arbre
    Par Bownobo dans le forum Langage
    Réponses: 3
    Dernier message: 09/09/2009, 14h46
  3. Comment représenter les arbres ?
    Par karray_ali dans le forum MATLAB
    Réponses: 2
    Dernier message: 12/02/2007, 01h40
  4. [POO] Représentation d'arbre (Nested Tree)
    Par zoullou dans le forum Langage
    Réponses: 3
    Dernier message: 20/06/2006, 16h27
  5. représentation en arbre
    Par GAGNON dans le forum Access
    Réponses: 6
    Dernier message: 26/10/2005, 23h25

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