Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Trees in RDBs

187 views

Published on

The story of one practical task.

Published in: Software
  • Be the first to comment

  • Be the first to like this

Trees in RDBs

  1. 1. Древовидная структура в PostgreSQL. SQL CommonДревовидная структура в PostgreSQL. SQL Common Table ExpressionTable Expression   (Алексей Кутепов, (Алексей Кутепов, Revel Systems iPad POSRevel Systems iPad POS))
  2. 2. Древовидные структуры: • Категории/подкатегории; • Комментарии в соцсетях; • Датацентр/комната/сервер/клиент/сервис; • Организационная иерархия; • … • RESTfull API Resources as a Tree
  3. 3. Room 1 Room 2 Server 1 Server 2 Server 3 Server 4 Cluster 1 Cluster 2 Cluster 3 Cluster 4 Cluster 6Cluster 5
  4. 4. Подходы к хранению деревьев в RDB • Adjacency List id parent_id name 1 NULL Room 1 2 1 Server 1 3 1 Server 2 4 3 Cluster 3
  5. 5. Свойства Adjacency List: • Удобно вставлять элементы • Переносить поддерево • Легко получать непосредственных потомков/предков • Трудно получать поддерево (но если у вас PostgreSQL…) INSERT INTO resources (parent_id, name) VALUES (2, ‘Cluster 1’); UPDATE resources SET parent_id = 2 WHERE id=4; SELECT * FROM resources r_left LEFT JOIN resources r_right ON (r_left.id = r_right.parent);
  6. 6. WITH Queries. Common Table Expression (CTE) • В стандарте SQL-99 • Поддеживается PosgreSQL, Oracle 11g, Microsoft SQL Server, … • Не поддерживается MySQL, SQLLite • CTE вводит вспомогательный statement, который можно представить как временную таблицу, которая существует только для одного, последующего сразу за ним запроса.
  7. 7. • WITH RECURSIVE - Using RECURSIVE, a WITH query can refer to its own output. WITH RECURSIVE t(n) AS ( VALUES (1) UNION ALL SELECT n+1 FROM t WHERE n < 100 ) SELECT sum(n) FROM t; “Note: Strictly speaking, this process is iteration not recursion, but RECURSIVE is the terminology chosen by the SQL standards committee.”
  8. 8. Свойства Adjacency List: • Легко получать поддерево используя CTE WITH RECURSIVE resources_cte_tree(id, parent_id, name, depth) AS ( SELECT *, 0 AS depth FROM resources WHERE parent_id = 5 UNION ALL SELECT r.*, rct.depth+1 AS depth FROM resources_cte_tree rct JOIN resources ON (rct.id = resources.id) ) SELECT * FROM resources_cte_tree;
  9. 9. • Path Enumeration id path name 1 /1 Room 1 2 /1/2 Server 1 3 /1/3 Server 2 4 /1/2/4 Cluster 3
  10. 10. Свойства Path Enumeration (Breadcrumbs thumbsup): • Легко вставлять элементы. • Легко получать непосредственных потомков/предков • Легко получать поддерево • Затратно перемещать поддерево SELECT * FROM resources r_left LEFT JOIN resources r_right ON (r_left.id = r_right.parent); SELECT * FROM resources WHERE path LIKE ‘/room/1/%’; INSERT INTO resources (name) VALUES (‘Rack ’) RETURNING id; SELECT path as parent_path FROM resources WHERE id = 1; UPDATE resources SET path = parent_path || ‘/’ || id;
  11. 11. Nested Sets • Каждый ресурс «кодирует» свои дочерние ресурсы 2 ключами-числами: • «Левое» число ресурса всегда меньше чем левое число его потомков • «Правое» число ресурса всегда больше, чем «правое» число его потомков
  12. 12. Id: 1 Room 1 Id: 2 Server 1 Id: 3 Server 2 Id: 4 Cluster 1 Id: 5 Cluster 2 Id: 6 Cluster 3 1 12 72 8 11 3 4 5 6 9 10
  13. 13. • Nested Sets id left_key right_key name 1 1 12 Room 1 2 2 7 Server 1 3 8 11 Server 2 4 3 4 Cluster 1 5 5 6 Cluster 2 6 9 10 Cluster 3
  14. 14. Свойства Nested Sets: ● Легко получать поддерево потомков/предков ● Трудно получить непосредственных предков/потомков (рецепт – добавить поле parent_id и/или depth) ● Трудно вставлять элемент ● Трудно перемещать дерево ● Можно хранить только одно дерево в одной таблице SELECT id, name FROM resources WHERE left_key >= $left_key AND right_key  <= $right_key ORDER BY left_key SELECT id, name, level FROM  resources  WHERE left_key  <= $left_key ANDright_key >= $right_key ORDER BY left_key
  15. 15. • Closure Table (M2M)id name 1 Room 1 2 Server 1 3 Server 2 4 Cluster 1 5 Cluster 2 6 Cluster 3 7 Room 2 8 Server 3 9 Server 4 10 Cluster 4 11 Cluster 5 12 Cluster 6 id parent_id child_id 1 NULL 1 2 1 2 3 1 3 4 2 4 5 2 5 6 3 6 7 NULL 7 8 7 8 9 7 9 10 8 10 11 8 11 12 8 12 Room 1 Room 2 Server 1 Server 2 Server 3 Server 4 Cluster 1 Cluster 2 Cluster 3 Cluster 4 Cluster 6 Cluster 5
  16. 16. Свойства Closure Table • Легко запрашивать прямых потомков/предков • Легко вставлять новые элементы • Легко удалять элементы • Трудно запрашивать поддерево SELECT r.* FROM resources r JOIN resources_tree t ON (r.id = t.child_id)  WHERE r.id = 1; SELECT r.* FROM resources r JOIN resources_tree t ON (r.id = t.parent_id)  WHERE r.id = 1; INSERT INTO resources (name) VALUES (‘Room 3’) RETURNING id; INSERT INTO resources_tree (parent_id, child_id) VALUES (NULL, id); DELETE FROM resources_tree WHERE parent_id = 1; DELETE FROM resources WHERE id = 1;
  17. 17. Свойства Closure Table • Легко запрашивать поддерево SQL CTE (SQL Alchemy) def get_subtree(root_id):   tree_query = (     db_base.db().query(Resource.id)     .join(ResourcesTree,           Resource.id == tree2resource_relation)     .filter(ResourcesTree.parent_id == root_id)     .cte(recursive=True)   )   tree_query = tree_query.union(     db_base.db().query(Resource.id)     .join(ResourcesTree, Resource.id == ResourcesTree.child_id)     .join(tree_query, tree_query.c.id == ResourcesTree.parent_id)   )   return (db_base.db().query(models.Resource)           .filter(tree_query.c.id == models.Resource.id))
  18. 18. Запрос прямых потомков Запрос поддерева Вставка нового элемента Изменение поддерева Adjacency List Легко Трудно* Легко Трудно Path Enumeration Легко Легко Легко Трудно Nested Sets Трудно** Легко Трудно Трудно Closure Table Легко Трудно*** Легко Легко * Легко, при использовании SQL CTE ** Легко, при введении полей parent_id и/или depth *** Легко, при использовании SQL CTE, либо при добавлении ссылок к каждому потомку (не всегда возможно)
  19. 19. Hierarchical and recursive queries in SQL CONNECT BY is supported by EnterpriseDB, Oracle database, CUBRID, IBM Informix and DB2
  20. 20. Informative links: Slides by Bill Karwin (of course): http://www.slideshare.net/billkarwin/sql-antipatterns-strike-back/ http://www.slideshare.net/billkarwin/models-for-hierarchical-data Nested Sets: http://www.getinfo.ru/article610.html Closure Table: https://habrahabr.ru/post/263629/ https:// en.wikipedia.org/wiki/Hierarchical_and_recursive_queries_in_SQL
  21. 21. Normative links: https:// www.postgresql.org/docs/current/static/queries-with.html SQL-99 standards between 1999-2002: ISO/IEC 9075-1:1999 ISO/IEC 9075-2:1999 ISO/IEC 9075-5:1999

×