67 lines
3.1 KiB
Plaintext
67 lines
3.1 KiB
Plaintext
Nested sets
|
|
===========
|
|
|
|
Je continue ma lecture du livre [SQL Antipatterns](https://pragprog.com/book/bksqla/sql-antipatterns) de Bill Karwin... Il y a plusieurs manières de représenter une structure hiérarchique dans une base de données.
|
|
|
|
http://www.slideshare.net/billkarwin/models-for-hierarchical-data
|
|
|
|
Un noeud racine A possède deux enfants, `B` et `E`, qui ont chacun respectivement les enfants C et D, puis F:
|
|
|
|
```
|
|
A
|
|
|---B
|
|
| |---C
|
|
| |---D
|
|
|---E
|
|
|---F
|
|
```
|
|
|
|
## Parent-enfant
|
|
|
|
Ca, c'est la modélisation la plus simple, mais favorise l'écriture et qui flingue la lecture. Chaque enregistrement connait l'identifiant de son parent. C'est cette méthode que je présentais dans le document {{< relref "2013-05-23-breadcrumbs.md" >}}. A l'époque, cela convenait très bien, jusqu'au moment où on arrive à plusieurs milliers de données, croisées avec plusieurs milliers de personnes... :)
|
|
|
|
Avec Django, cela revient simplement à écrire un truc comme ceci:
|
|
|
|
```python
|
|
class Node(models.Model):
|
|
label =
|
|
```
|
|
|
|
## Enumération du chemin d'accès
|
|
|
|
|
|
|
|
## Nested Sets
|
|
|
|
Les *nested sets* sont une des manières de répondre à ce problème dans une base de données relationnelle.
|
|
|
|
Une [librairie Django (Django-mptt)](http://django-mptt.readthedocs.io/en/stable/) implémente très bien ce concept, au travers du pattern `Modified Pre-ordre Tree Traversal`, présenté par Gijs Van Tulder dans son article [Storing Hierarchical Data in Database](http://www.sitepoint.com/hierarchical-data-database/), en 2003.
|
|
|
|
Le fonctionnement est le suivant: lors du parcours préfixe de l'arbre, les valeurs `gauche` et `droite` sont attribuées à chaque noeud, sur base d'un incrément. Sur base de l'arborescence présentée ci-dessus, on obtiendra le résultat suivant:
|
|
|
|
```
|
|
A [Gauche: 1; Droite 12]
|
|
|---B [Gauche 2; Droite 7]
|
|
| |---C [Gauche 3; Droite 4]
|
|
| |---D [Gauche 5; Droite 6]
|
|
|---E [Gauche 8; Droite 11]
|
|
|---F [Gauche 9; Droite 10]
|
|
```
|
|
|
|
Suivant que l'on souhaite obtenir les descendants ou ascendants, il suffit de jouer sur les valeurs gauches/droites de la manière suivante:
|
|
|
|
* Pour tous les descendants de B, on prend tous les noeuds dont les valeurs gauche/droite sont comprises entre les valeurs **2** et **7**.
|
|
* Pour E, on prend l'intervalle **[8, 11]**
|
|
* Pour les ascendants du noeud D, on prend les noeuds dont les valeurs gauche/droite sont plus grandes (mais contiennent!) l'intervalle **[5, 6]**: cela nous rend A et B, mais pas C car le segment [3, 4] n'est pas compris dans l'intervalle [5, 6].
|
|
|
|
La lecture d'un groupe d'informations est extrêmement rapide. On peut l'accélérer L'écriture l'est tout de suite moins: à chaque insertion ou modification de l'arbre, les feuilles doivent être recalculées, ce qui engendre un *overhead* considérable.
|
|
|
|
## Closure
|
|
|
|
https://stackoverflow.com/questions/14789046/django-orm-and-closure-tables#26626855
|
|
|
|
## Sources
|
|
|
|
* [Stackoverflow](https://stackoverflow.com/questions/8196175/managing-hierarchies-in-sql-mptt-nested-sets-vs-adjacency-lists-vs-storing-path?noredirect=1&lq=1)
|
|
* [Stackoverflow²](https://stackoverflow.com/questions/192220/what-is-the-most-efficient-elegant-way-to-parse-a-flat-table-into-a-tree/192462#192462)
|