grimboite/old/2016-05-10-nested-sets.draft

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)