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

VBA Access Discussion :

Nested set, noeuds et parents [AC-2016]


Sujet :

VBA Access

  1. #1
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut Nested set, noeuds et parents
    Bonjour,

    Je sollicite votre aide:
    Dans une BDD où je gère un arbre sur le modèle des sets imbriqués (https://mikehillyer.com/articles/man...data-in-mysql/),
    je cherche à récupérer, pour construire un treeview, l'identifiant des nœuds, et s'il existe l'identifiant du parent immédiat.

    J'accepte les solutions SQL et VBA.

  2. #2
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    Bonjour deedolith,

    Je ne suis pas certain de bien comprendre votre question, si je prend l'exemple du lien que vous donnez, l'identifiant du nœud est "category_id" et le parent immédiat est "parent". Ces deux champs ne sont pas valables pour un TreeView puisque le premier caractère doit être alpha il suffit donc de concaténer les "id" avec une lettre et vous avez votre TreeView.

    Voici un exemple de "Sub" dans un module standard qui peut être appelé pour remplir un TreeView:
    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
    Option Compare Database
    Option Explicit
     
    Public Sub loadTreeviewDD(oForm As Access.Form, sTV_Name As String)
    On Error GoTo err_Handler
     
    Dim tv                          As MSComctlLib.TreeView
    Dim nod                         As MSComctlLib.Node
    Dim odb                         As DAO.Database
    Dim rst                         As DAO.Recordset
    Dim strSQL                      As String
     
       Set tv = oForm(sTV_Name).Object
       With tv
            .Nodes.Clear
            .Font.Size = 11
            .Font.Name = "Arial"
     
           strSQL = "SELECT T_Category.* FROM T_Category;"
           Set odb = CurrentDb
           Set rst = odb.OpenRecordset(strSQL, 4, 512)
           Do While Not rst.EOF
           If IsNull(rst("category_parent")) Then
              Set nod = .Nodes.Add(, , "K" & rst("category_id"), rst("category_name"))
              nod.Bold = True
           Else
              Set nod = .Nodes.Add("K" & rst("category_parent"), tvwChild, "K" & rst("category_id"), rst("category_name"))
           End If
              rst.MoveNext
           Loop
       End With
     
     
    Sortie:
    On Error Resume Next
       rst.Close
       Set rst = Nothing
       Set odb = Nothing
       Set nod = Nothing
       Set tv = Nothing
    Exit Sub
     
    err_Handler:
        fuErr_Handler ("loadTreeviewDD subRoutine") 'À remplacer par votre gestion d'erreur
        Resume Sortie
    End Sub
    Les arguments sont le formulaire et le nom du TreeView.

    Bonne journée

  3. #3
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    Effectivement, tu n'as pas compris, la requête que tu proposes se contente de lister les noeuds sans considération hiérarchique.

    C'est à partir de la section The Nested Set Model / Retrieving a Full Tree que ca m'intéresse.
    Récupérer l'arbre, ok. mais il me manque les informations des noeuds parents pour construire le treeview.

    PS: Construire les clefs des noeuds n'est pas un problème (concatener une chaîne quelconque avec l'identifiant d'un noeud).

  4. #4
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    Bonjour deedolith,

    Est-ce bien un Contrôle ActiveX "CTreeView Control" que vous désirez? Si c'est bien le cas, il n'est pas nécessaire d'avoir la hiérarchie complète dans la requête. Le contrôle va faire le travail, il suffit d'avoir le nœud parent. Si on reprend la table du lien indiqué dans votre post #1 voici le résultat:
    Nom : TVDeedolith.png
Affichages : 82
Taille : 10,8 Ko

    Bonne journée

  5. #5
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    Ce n'est pas le treeview qui me pose problème, mais les données:
    - Identifiant d'un noeud ==> Ok
    - Identifiant du noeud parent ==> ?????

    Et entre la requête que tu proposes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    SELECT T_Category.* FROM T_Category;
    et celle présente sur le site:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    SELECT node.name
    FROM nested_category AS node,
            nested_category AS parent
    WHERE node.lft BETWEEN parent.lft AND parent.rgt
            AND parent.name = 'ELECTRONICS'
    ORDER BY node.lft;
    Excuse moi de constater un differentiel dont l'explication m'echappe.

  6. #6
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    Bonjour deedolith,

    Ok je n'utilisait pas la bonne table. Avec ce genre de structure, j'utiliserais une fonction récursive, dans un module standard avec la même procédure d'appel:
    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    Option Compare Database
    Option Explicit
     
    Public Sub loadTreeviewNested(oForm As Access.Form, sName As String)
    On Error GoTo err_Handler
     
    Dim tv                          As MSComctlLib.TreeView
    Dim nod                         As MSComctlLib.Node
    Dim odb                         As DAO.Database
    Dim rst                         As DAO.Recordset
    Dim strSQL                      As String
    Dim sFind                       As String
    Dim sBook                       As String
     
       Set tv = oForm(sName).TVMenu.Object
       With tv
            .Nodes.Clear
            .Font.Size = 12
            .Font.Name = "Arial"
     
           strSQL = "SELECT Nested_Category.* FROM Nested_Category;"
           Set odb = CurrentDb
           Set rst = odb.OpenRecordset(strSQL, 4, 512)
           sFind = "lft=1"  'Premier niveau
           rst.FindFirst sFind
     
           Do While Not rst.NoMatch
              Set nod = .Nodes.Add(, , "K" & rst("category_id"), rst("category_name"))
              nod.Bold = True
              sBook = rst.Bookmark
              addChildren tv, nod, rst, rst("lft"), rst("rgt")
              rst.Bookmark = sBook
              rst.FindNext sFind
           Loop
       End With
       rst.Close
       Set rst = Nothing
       Set nod = Nothing
       Set tv = Nothing
     
    Sortie:
    On Error Resume Next
       rst.Close
       Set rst = Nothing
       Set odb = Nothing
       Set nod = Nothing
       Set tv = Nothing
    Exit Sub
        Exit Sub
     
    err_Handler:
        fuErr_Handler ("loadTreeview Function") 'Votre gestion d'erreur
        Resume Sortie
    End Sub
     
    Private Sub addChildren(tv As TreeView, nodParent As Node, rst As DAO.Recordset, lLft As Long, lRgt As Long)
    On Error GoTo err_Handler
     
    Dim nodX                        As Node
    Dim sBook                       As String
    Dim sFind                       As String
     
        sFind = "lft>" & lLft & " AND rgt<" & lRgt
        rst.FindFirst sFind
        Do While Not rst.NoMatch
          Set nodX = tv.Nodes.Add(nodParent, tvwChild, "K" & rst("category_id"), rst("category_name"))
          sBook = rst.Bookmark
          addChildren tv, nodX, rst, rst("lft"), rst("rgt")
          rst.Bookmark = sBook
          rst.FindNext sFind
       Loop
    Sortie:
    On Error Resume Next
        Exit Sub
     
    err_Handler:
        fuErr_Handler ("addChildren Function")  'Votre gestion d'erreur
        Resume Sortie
    End Sub
    Bonne journée

  7. #7
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    Ok, je voit a peut près comment tu faits: récursivité (des explications n'auraient pas été superflues).

    Par contre, dans mon cas, je peux avoir plusieurs nœuds racine, la table n'est pas nécessairement triée, et la colonne lft des noeuds racine ne commence pas nécéssairement à 1. D'où la nécessité de clairement identifier les relations parent / enfant.
    Considère la table suivante, après X insertions / suppressions / déplacements de noeuds:
    category_id name lft rgt
    2 PORTABLE ELECTRONICS 9 18
    3 TUBE 20 21
    6 MP3 PLAYERS 10 13
    7 CD PLAYERS 14 15
    9 FLASH 11 12
    1 TELEVISIONS 19 26
    5 PLASMA 24 25
    8 2 WAY RADIOS 16 17
    4 LCD 22 23
    PORTABLE ELECTRONICS est le premier nœud racine, TELEVISIONS est le second;

  8. #8
    Membre chevronné Avatar de Thumb down
    Homme Profil pro
    Retraité
    Inscrit en
    Juin 2019
    Messages
    1 468
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Juin 2019
    Messages : 1 468
    Points : 2 235
    Points
    2 235
    Par défaut
    Bonjour,
    requête récursive pour récupérer les catégories et leurs sous-catégories dans MySQL 8.0 ou supérieur
    Code SQL : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    WITH RECURSIVE category_tree AS (
     
        SELECT id, nom, parent_id
        FROM categories
        WHERE parent_id IS NULL -- Sélectionne les catégories racines (celles qui n'ont pas de parent)
        UNION ALL
        SELECT c.id, c.nom, c.parent_id
        FROM categories c
        JOIN category_tree ct ON c.parent_id = ct.id -- Sélectionne les sous-catégories en faisant une jointure avec le CTE lui-même
    )
    SELECT * FROM category_tree; -- Sélectionne toutes les lignes du CTE pour afficher les catégories et leurs sous-catégories
    Cette requête utilise une construction récursive pour récupérer toutes les catégories et leurs sous-catégories. Voici une explication détaillée de chaque partie :

    • La clause WITH RECURSIVE permet de définir un CTE (Common Table Expression) récursif nommé category_tree.
    • Dans la première partie du CTE (la partie avant l'opérateur UNION ALL), nous sélectionnons les catégories racines de la table categories. Celles-ci sont identifiées par le fait qu'elles n'ont pas de valeur dans la colonne parent_id.
    • L'opérateur UNION ALL combine les résultats de la première partie avec ceux de la deuxième partie du CTE.
    • Dans la deuxième partie du CTE (la partie après l'opérateur UNION ALL), nous sélectionnons les sous-catégories en joignant la table categories avec le CTE category_tree. Cela nous permet de récupérer les sous-catégories pour chaque catégorie déjà sélectionnée.
    • La clause SELECT * FROM category_tree en dehors du CTE sélectionne toutes les lignes du CTE category_tree, ce qui permet d'afficher toutes les catégories et leurs sous-catégories.

    En résumé, cette requête récursive récupère toutes les catégories et leurs sous-catégories, peu importe le niveau de profondeur de la hiérarchie.

    aux vue des autre réponse il semblerait que je suis à coté de la plaque

  9. #9
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    @ThumbDown: On est sous Ms Access ...

  10. #10
    Membre chevronné Avatar de Thumb down
    Homme Profil pro
    Retraité
    Inscrit en
    Juin 2019
    Messages
    1 468
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Rhône (Rhône Alpes)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Juin 2019
    Messages : 1 468
    Points : 2 235
    Points
    2 235
    Par défaut
    Citation Envoyé par Thumb down Voir le message
    Bonjour,
    ...
    aux vue des autre réponse il semblerait que je suis à coté de la plaque
    je me suis fié au lien et j'ai bien compris après que je m'étai planté!
    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
    Public Sub RecursiveQuery()
        Dim strSQL As String
        Dim rst As Recordset
     
        ' Créer une table temporaire pour stocker les résultats
        DoCmd.RunSQL "CREATE TABLE TempCategoryTree (id INT, nom TEXT, parent_id INT)"
     
        ' Insérer les catégories racines dans la table temporaire
        DoCmd.RunSQL "INSERT INTO TempCategoryTree (id, nom, parent_id) SELECT id, nom, parent_id FROM categories WHERE parent_id IS NULL"
     
        ' Répéter la logique récursive jusqu'à ce qu'il n'y ait plus de nouvelles lignes à ajouter
        Do
            ' Insérer les sous-catégories dans la table temporaire en fonction des catégories déjà présentes
            strSQL = "INSERT INTO TempCategoryTree (id, nom, parent_id) " & _
                     "SELECT c.id, c.nom, c.parent_id " & _
                     "FROM categories AS c " & _
                     "INNER JOIN TempCategoryTree AS t ON c.parent_id = t.id " & _
                     "WHERE c.id NOT IN (SELECT id FROM TempCategoryTree)"
            DoCmd.RunSQL strSQL
        Loop Until DCount("*", "categories", "parent_id NOT IN (SELECT id FROM TempCategoryTree)") = 0
     
        ' Afficher les résultats
        Set rst = CurrentDb.OpenRecordset("SELECT * FROM TempCategoryTree")
        Do While Not rst.EOF
            Debug.Print rst!id, rst!nom, rst!parent_id
            rst.MoveNext
        Loop
        rst.Close
     
        ' Supprimer la table temporaire
        DoCmd.RunSQL "DROP TABLE TempCategoryTree"
    End Sub

  11. #11
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    Je pense que tout comme Robert1957, tu n'es pas sur la bonne table.
    Ce que tu proposes concerne le modèle des "listes adjacentes" (The Adjacency List Model) où une table se référence elle-même,
    grand classique en SQL, quand on connait le niveau maximum de la hiérarchisation, ce qui n'est pas mon cas.

    Je suis sur le modèle des "listes imbriquées" (The Nested Set Model)
    .

  12. #12
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    Bonjour deedolith,

    En utilisant la table du post #7 vous devez modifier la ligne 21 du post #6 ainsi (c'était d'ailleurs un oubli de ma part):
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    strSQL = "SELECT Nested_Category.* FROM Nested_Category ORDER BY Nested_Category.lft;"
    Comme le principe est d'avoir disons 1 pour la gauche et 20 pour la droite (premier nœud) et ensuite 21 et 40 (pour le deuxième nœud) et ainsi de suite pour les nœuds enfants en ordonnant la requête ainsi on retrouvera les nœuds dans le bon ordre.

    Ensuite la ligne 24 pour inclure tous les premiers niveaux désirés. C'est d'ailleurs pour cette raison que le code est ainsi fait. Pour mon usage j'avais besoin d'afficher plusieurs premiers niveaux dans le TreeView.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sFind = "category_name In (""PORTABLE ELECTRONICS"",""TELEVISIONS"")"
    Vous pourrez ainsi afficher les 2 catégories (nœuds racine) que vous avez spécifiées. S'il y avait plus de catégories vous n'auriez qu'a ajouter les dites catégories.

    Le sub "addChildren" est récursif et va creuser les niveaux tant qu'il y en aura pour chacune des catégories.

    Il faut cependant ajouter une gestion d'erreur, pour attraper le cas ou une sous catégorie serait traitée comme une catégorie ex: "FLASH" dans la table du post #7 ce qui ne serait pas un problème puisqu'on a ordonné la requête de la bonne façon.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    err_Handler:
        If Err.Number = 35602 Then
            Resume Next
        Else
            fuErr_Handler ("addChildren Function")
            Resume Sortie
        End If
    J'espère que ça peut vous aider.

    J'ai cependant une question, je viens de découvrir et je comprend le principe théorique du Nested Set Model qui est super mais dans la réalité quand est il avantageux de l'utiliser?

    Bonne journée

  13. #13
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    Citation Envoyé par Robert1957 Voir le message
    Ensuite la ligne 24 pour inclure tous les premiers niveaux désirés. C'est d'ailleurs pour cette raison que le code est ainsi fait. Pour mon usage j'avais besoin d'afficher plusieurs premiers niveaux dans le TreeView.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sFind = "category_name In (""PORTABLE ELECTRONICS"",""TELEVISIONS"")"
    Je craint de ne pas te suivre.
    Si tu t'appuies sur des valeurs particulières, ca ne peut pas fonctionner, rien ne garantie leur présence.

  14. #14
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    Bonjour deedolith,

    Avec la méthode que je vous ai proposée, il faut s'appuyer sur des valeurs particulières pour les nœuds racine. Imaginons un TreeView qui afficherait des items par départements, "ELECTRONICS", "TELEVISIONS", "ÉLECTROMÉNAGERS", "AMEUBLEMENT" etc... La routine va rechercher les départements, si trouvé créer un nœud racine sinon on passe au suivant (loadTreeviewNested). Lorsque trouvé on appel le sub "addChildren" en spécifiant la limite inférieur (lft) et supérieur (rgt) du nœud racine, on parcours les enregistrements qui réponde à ces critères. Pour chaque enregistrement répondant à ces critères on relance la sub "addChildren" pour ainsi créer un nouveau niveau de nœud et ainsi de suite jusqu'au moment ou il n'y a plus d'enregistrements trouvés. Si un département était vide, c'est à dire un enregistrement pour "ÉLECTROMÉNAGERS" mais aucun enregistrement répondant aux critères de limite supérieur et inférieur on aurait un nœud racine sans enfant. Pour l'autre éventualité, si on recherche un noeud racine qui n'existe pas rien ne se passe.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    sFind = "category_name In (""ELECTRONICS"",""TELEVISIONS"",""ÉLECTROMÉNAGERS"",""AMEUBLEMENT"")"
    Voici le résultat de la table du post #7 avec ajout d'une ligne
    11/ÉLECTROMÉNAGERS/27/28 dans cette table:
    Nom : TVDeedolith1.png
Affichages : 58
Taille : 11,1 Ko

    Bonne journée

  15. #15
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    Ok, je comprend ton raisonnement, mais avec des données 100% dynamiques, s'appuyer sur des valeurs particulières n'est pas acceptables.
    De plus, un algorithme generique fonctionnera pour des cas particuliers, l'inverse n'est pas vrai.

    Le site de Mike Hillyer propose des requêtes dont une pour retournant l'arborescence des noeuds, mais il manque l'information du noeud parent lorsqu'il existe, c'est précisément ce que je recherche.

  16. #16
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    Bonjour deedolith,

    Si on prend en exemple la requête "We can use the depth value to indent our category names with the CONCAT and REPEAT string functions:" comme exemple, il suffit d'ajouter une gestion d'erreur comme suit dans la sub "loadTreeviewNested" et de parcourir tous les records pour alimenter le TreeView. La gestion d'erreur nous permet de récupérer les nœuds racines. Je n'ai pas fait le test dans une table qui aurait des milliers d'enregistrements mais bon...
    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    Option Compare Database
    Option Explicit
     
    Public Sub loadTreeviewNested(oForm As Access.Form, sName As String)
    On Error GoTo err_Handler
     
    Dim tv                          As MSComctlLib.TreeView
    Dim nod                         As MSComctlLib.Node
    Dim odb                         As DAO.Database
    Dim rst                         As DAO.Recordset
    Dim strSQL                      As String
    'Dim sFind                       As String n'est plus nécessaire
    Dim sBook                       As String
     
       Set tv = oForm(sName).Object
       With tv
            .Nodes.Clear
            .Font.Size = 12
            .Font.Name = "Arial"
     
           strSQL = "SELECT Nested_Category.* FROM Nested_Category ORDER BY Nested_Category.lft;"
           Set odb = CurrentDb
           Set rst = odb.OpenRecordset(strSQL, 4, 512)
    '       sFind = "category_name In (""ELECTRONICS"",""TELEVISIONS"",""ÉLECTROMÉNAGERS"",""AMEUBLEMENT"")"
    '       rst.FindFirst sFind
     
    '       Do While Not rst.NoMatch
            Do While Not rst.EOF
              Set nod = .Nodes.Add(, , "K" & rst("lft") & "|" & rst("rgt"), rst("category_name"))
              nod.Bold = True
              sBook = rst.Bookmark
              addChildren tv, nod, rst, rst("lft"), rst("rgt")
              rst.Bookmark = sBook
    '          rst.FindNext sFind
            rst.MoveNext
           Loop
       End With
       rst.Close
       Set rst = Nothing
       Set nod = Nothing
       Set tv = Nothing
     
    Sortie:
    On Error Resume Next
       rst.Close
       Set rst = Nothing
       Set odb = Nothing
       Set nod = Nothing
       Set tv = Nothing
    Exit Sub
        Exit Sub
     
    err_Handler:
        If Err.Number = 35602 Then 'ce qui permet de passer tous les records
            Resume Next
        Else
            fuErr_Handler ("addChildren Function")
            Resume Sortie
        End If
    End Sub
     
    Private Sub addChildren(tv As TreeView, nodParent As Node, rst As DAO.Recordset, lLft As Long, lRgt As Long)
    On Error GoTo err_Handler
     
    Dim nodX                        As Node
    Dim sBook                       As String
    Dim sFind                       As String
     
        sFind = "lft>" & lLft & " AND rgt<" & lRgt
        rst.FindFirst sFind
        Do While Not rst.NoMatch
          Set nodX = tv.Nodes.Add(nodParent, tvwChild, "K" & rst("lft") & "|" & rst("rgt"), rst("category_name"))
          sBook = rst.Bookmark
          addChildren tv, nodX, rst, rst("lft"), rst("rgt")
          rst.Bookmark = sBook
          rst.FindNext sFind
       Loop
    Sortie:
    On Error Resume Next
        Exit Sub
     
    err_Handler:
        If Err.Number = 35602 Then
            Resume Next
        Else
            fuErr_Handler ("addChildren Function")
            Resume Sortie
        End If
    End Sub
    J'ai laissé en commentaire les anciennes lignes pour qu'on voit bien la différence.
    De cette manière c'est complètement dynamique.

    Bonne journée

  17. #17
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    J'ai oublié de mentionner,

    j'ai aussi modifier la manière de créer la clé du nœud pour récupérer le lft et rgt de cette manière si on voulait travailler avec le TreeView (drag and drop ou autre) il serait possible de modifier les données de la table qui alimente le TreeView.

    Bonne journée

  18. #18
    Membre expert
    Homme Profil pro
    Consultant informatique
    Inscrit en
    Octobre 2012
    Messages
    1 873
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Consultant informatique
    Secteur : Service public

    Informations forums :
    Inscription : Octobre 2012
    Messages : 1 873
    Points : 3 459
    Points
    3 459
    Par défaut
    Bonjour deedolith,

    Un samedi après midi pluvieux, qu'est ce qu'on fait? On s'amuse en VBA.

    En regardant mon code, je trouvais qu'il n'était pas très bon. Voici une autre façon de faire qui filtre adéquatement les données et qui n'a pas besoin de béquilles.
    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
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    Option Compare Database
    Option Explicit
     
    Public Sub loadTreeviewNested(oForm As Access.Form, sName As String)
    On Error GoTo err_Handler
     
    Dim tv                          As MSComctlLib.TreeView
    Dim nod                         As MSComctlLib.Node
    Dim odb                         As DAO.Database
    Dim rst                         As DAO.Recordset
    Dim strSQL                      As String
    Dim sFind                       As String
    Dim sBook                       As String
     
       Set tv = oForm(sName).Object
       With tv
            .Nodes.Clear
            .Font.Size = 12
            .Font.Name = "Arial"
     
            strSQL = "SELECT node.category_id, node.category_name, node.lft, node.rgt, (Count(parent.category_name)-1) AS depth " _
            & "FROM nested_category AS node, nested_category AS parent " _
            & "WHERE node.lft Between [parent].[lft] And [parent].[rgt] " _
            & "GROUP BY node.category_id, node.category_name, node.lft, node.rgt, node.lft " _
            & "ORDER BY node.lft;"
           Set odb = CurrentDb
           Set rst = odb.OpenRecordset(strSQL, 4, 512)
           sFind = "depth=0"
           rst.FindFirst sFind
           Do While Not rst.NoMatch
              Set nod = .Nodes.Add(, , "K" & rst("category_id") & "|" & rst("lft") & "|" & rst("rgt"), rst("category_name"))
              nod.Bold = True
              sBook = rst.Bookmark
              addChildren tv, nod, rst, rst("lft"), rst("rgt"), rst("depth")
              rst.Bookmark = sBook
              rst.FindNext sFind
           Loop
       End With
       rst.Close
       Set rst = Nothing
       Set nod = Nothing
       Set tv = Nothing
     
    Sortie:
    On Error Resume Next
       rst.Close
       Set rst = Nothing
       Set odb = Nothing
       Set nod = Nothing
       Set tv = Nothing
    Exit Sub
        Exit Sub
     
    err_Handler:
        fuErr_Handler ("loadTreeviewNested Function")
        Resume Sortie
    End Sub
     
    Private Sub addChildren(tv As TreeView, nodParent As Node, rst As DAO.Recordset, lLft As Long, lRgt As Long, ByVal lDepth As Long)
    On Error GoTo err_Handler
     
    Dim nodX                        As Node
    Dim sBook                       As String
    Dim sFind                       As String
     
        sFind = "lft>" & lLft & " AND rgt<" & lRgt & " AND depth = " & lDepth + 1
        rst.FindFirst sFind
        Do While Not rst.NoMatch
          Set nodX = tv.Nodes.Add(nodParent, tvwChild, "K" & rst("category_id") & "|" & rst("lft") & "|" & rst("rgt"), rst("category_name"))
          sBook = rst.Bookmark
          addChildren tv, nodX, rst, rst("lft"), rst("rgt"), rst("depth")
          rst.Bookmark = sBook
          rst.FindNext sFind
       Loop
     
    Sortie:
        Exit Sub
     
    err_Handler:
        fuErr_Handler ("addChildren Function")
        Resume Sortie
    End Sub
    De plus en créant la clé comme je fais il est possible de retrouver ou travailler avec celle-ci comme suit:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    Public Function getID(nodX As MSComctlLib.Node) As String
    On Error GoTo err_Handler
     
        Dim sKey As String
        sKey = nodX.Key
        getID = Right(sKey, Len(sKey) - 1)
     
    Sortie:
        Exit Function
     
    err_Handler:
        fuErr_Handler ("getID Function")
        Resume Sortie
    End Function
    Ce qui nous renvoie un tableau "4|22|23" pour la dernière ligne de votre post #7.

    Bonne journée

  19. #19
    Membre chevronné
    Profil pro
    Inscrit en
    Juillet 2006
    Messages
    1 339
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Juillet 2006
    Messages : 1 339
    Points : 1 955
    Points
    1 955
    Par défaut
    Ok merci, testé et approuvé.

    Pour répondre à ta question précédente:
    Citation Envoyé par Robert1957 Voir le message
    J'ai cependant une question, je viens de découvrir et je comprend le principe théorique du Nested Set Model qui est super mais dans la réalité quand est il avantageux de l'utiliser?
    L'avantage et que l'on peut construire une hiérarchie de profondeur illimitée.
    Le désavantage par contre, et que la mise à jour de la hiérarchie (ajout / suppression de nœuds) entraine la mise à jour toute la table.
    Sur de grandes quantité de données, il y a un impacte non négligeable sur les performances.

    Bref: Ca dépend des cas.

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

Discussions similaires

  1. [Doctrine] Comment bouger les noeud avec les Nested Set
    Par beachjf dans le forum PHP & Base de données
    Réponses: 0
    Dernier message: 10/02/2011, 21h31
  2. Réponses: 10
    Dernier message: 26/08/2010, 01h11
  3. Réponses: 1
    Dernier message: 23/08/2010, 11h10
  4. Nested set - Treeview
    Par Erakis dans le forum ASP.NET
    Réponses: 6
    Dernier message: 28/07/2010, 18h54
  5. [1.x] Nested set avec jointure
    Par damiensan dans le forum Symfony
    Réponses: 4
    Dernier message: 21/05/2010, 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