Имя: Пароль:
IT
 
Иерархия в базе данных
,
0 Волшебник
 
модератор
06.03.17
22:17
Для отображения иерархии в базе данных часто используют поле "Родитель", которое содержит ID родителя. Например, {ID, name, parent}, где parent - такое же значение как ID.

Для _хранения_ иерархии этого вполне достаточно, но возникают сложности с отображением и выборкой такой иерархии. Уж слишком сложные функции получаются просто для отображения дерева. Ведь нужно посчитать уровень элемента, а значит нужно пробежаться по родителям до корня в цикле. Для вывода дерева часто используется рекурсия, которая может свалиться в бесконечность и вызвать крах приложения и даже отладчика.

Есть и другие методы решения этих проблем, например, нарушить нормализацию и завести поле "Родители" (полный путь), содержащее коды всех родителей через слэш. Или ещё более надёжный вариант: создать отдельную таблицу с отношениями между элементами (аналог ClosureTable), которая содержит ID предка и ID потомка любой вложенности в одной записи. Это ускоряет все запросы на выборку подчинённых элементов заданного родителя, но замедляет обновление таблицы.

Что посоветуете для элегантного решения проблемы иерархии справочников в контексте разработки мобильного приложения для Android на языке Java, где базой управляет SQLite?
1 vde69
 
06.03.17
22:28
я так рассуждаю
1. андроид - значит мало памяти
2. оптимизация - на чтение или на запись, скорее на чтение

любые дополнительные таблицы/поля - жрут память

значит остается классический вариант с родителем

либо изменять изначальные условия 1 и 2
2 Волшебник
 
модератор
06.03.17
22:37
(1) В любом смартфоне уже 2-3 Гб есть памяти, а скоро будет 4-6. Размер базы данных конкретно для моего приложения не имеет значения. Там будут чисто цифры и тексты (учёт домашних финансов), а всё это мало места занимает (это же не картинки и не видео).
3 Неверный Параметр И
 
06.03.17
22:40
4 Неверный Параметр И
 
06.03.17
22:41
Там методы на любой вкус с анализом
5 Волшебник
 
модератор
06.03.17
22:43
(3) Ага, спасибо за ссылку. Это путь в правильном направлении.
6 Неверный Параметр И
 
06.03.17
22:43
Я бы остановился на классических nested sets
7 Волшебник
 
модератор
06.03.17
22:45
(6) Больше интересуют планы запросов и вложенные циклы. Лишь бы не получилось запросов в цикле. Самая страшная страшилка 1С-ников...
8 vde69
 
06.03.17
22:46
(2) тогда делай 3 таблицы
1. справочник без групп, у элемента поле родитель (ссылка на вторую таблицу)
2. рубрикатор (справочник групп)
3. связи элементов рубрикатора

простое чтение внутри группы - джойн 1+2
построение дерева на форме - 3+2 (тут возможны проблеммы при рекурсии и не однозначных связях)
запись элемента - только таб 1
перенос элемента в друг группу - 1+3
перенос группы - таб 3
9 Волшебник
 
модератор
06.03.17
22:49
(8) А не слишком ли это круто для простого иерархического справочника? Мне кажется, что даже 2 таблицы уже круто, а ты сразу 3 таблицы зарядил...
10 vde69
 
06.03.17
22:52
(9) зато соответствует правилам нормализации - все три таблицы не содержат неиспользуемых полей.

таблица 2. - будет вообще милипусенькая, да и таб 3 то-же не большая
11 Волшебник
 
модератор
06.03.17
22:53
(10) Можешь привести структуру таблиц для большего пониманиям этого беспредела?...
12 Волшебник
 
модератор
06.03.17
22:56
Параллельно я буду приводить свои изыскания на эту тему.

Например, типичная задача: "Выбрать все подчинённые элементы заданного родителя (с учётом вложенности)" реализуется запросом:

SELECT * FROM Expenses WHERE guid = "AA1" OR parents LIKE "%AA1%"

О, боже! Это же супер-тормоз! Да, в моём случае сработает, но что будем делать с другими случаями?
13 vde69
 
06.03.17
22:58
таб 1 - Name, ID_P
таб 2 - ID, Name
таб 3 - ID_1, ID_2

полное дерево джойнится
1.ID_P = 2.ID
2.ID = 3.ID_1
3.ID_1 = 3.ID_2
14 Волшебник
 
модератор
06.03.17
22:59
(13) А что за волшебная фея заполняет таб2 и таб3?
15 vde69
 
06.03.17
23:00
(13) + что-то наврал :)
16 vde69
 
06.03.17
23:04
может так

таб 1 - Name, ID_P
таб 2 - ID, Name, ID_P

1.ID_P = 2.ID
2.ID = 2.ID_P

то есть это то же что и в 1с, только разделили на 2 таблицы для того, что бы не было не используемых полей.
17 Asmody
 
06.03.17
23:09
храни структуру в отдеьном json-файле
18 Asmody
 
06.03.17
23:20
Вот https://habrahabr.ru/post/46659/ про разные способы представления иерархии в реляционных базах
19 Волшебник
 
модератор
06.03.17
23:23
(18) Хорошая статья. Читаю
20 Волшебник
 
модератор
07.03.17
10:38
Временное решение пока такое:

В таблице иерархического справочника есть исходные поля:
parent - id родителя,
position - число, позиция в пределах группы,
и дополнительные поля:
folder - путь от корня до вышестоящей папки (по умолчанию "/")
level - число, глубина вложенности
ordinal - особая строка для сортировки, содержащая позиции и id всех родителей + собственную позицию и id

Пример ordinal:
/#00001#C25/#00002#AA3/#00001#BF6
где
#00001#C25 - папка с ID=C25 и позицией №1
#00002#AA3 - папка AA3 с позицией №2
#00001#BF6 - сам элемент BF6 с позицией №1

В программном коде есть добрая фея, которая заполняет все эти дополнительные реквизиты на основе parent и position. Т.е. при записи элемента справочника прилетает фея и своей волшебной палочкой перенумеровывает все элементы справочника.

Запись замедляется, но в итоге запрос вывода дерева с учётом позиции внутри группы становится тривиальным и не требует пост-обработки в программном коде:

SELECT *, folder || ID AS FullPath
FROM Catalog
ORDER BY ordinal ASC
21 Волшебник
 
модератор
07.03.17
10:41
(20)+ картинко
иерархия
22 Naf2017
 
07.03.17
13:59
вот иерархия как вложенные интервалы http://www.codenet.ru/db/other/trees/
23 arsik
 
гуру
07.03.17
14:04
У нас уровень сразу хранится в элементе, как доп поле. Спасает неплохо.
24 arsik
 
гуру
07.03.17
14:06
(20) Ты изобрел клюшки?
25 Asmody
 
07.03.17
14:07
(24) Всё уже придумано до нас
26 Волшебник
 
модератор
07.03.17
14:08
(24) Я не в курсе, что там было в клюшках.
27 arsik
 
гуру
07.03.17
14:21
Можно гибрид сделать.
В элементе хранить только родителя, а в группах хранить всю иерархию.
28 arsik
 
гуру
07.03.17
14:33
Запись ускорится, запрос ненамного замедлится
29 Волшебник
 
модератор
07.03.17
14:34
(27) Похоже на химеру
30 SanGvin
 
07.03.17
14:35
а если всю иерархию хранить в таблице вида
id элемента, id родителя, уровень
Можно одним джойном выбрать всех родителей
31 Волшебник
 
модератор
07.03.17
14:40
(30) Да, так можно. Это называется ClosureTable
https://habrahabr.ru/post/193166/
32 SanGvin
 
07.03.17
14:42
имхо самый оптимальный вариант если скорость выборки важнее скорости записи
33 Живой Ископаемый
 
07.03.17
14:42
(0) Не иметь для мобильной базы глубину иерархии более 1 уровня.
34 Asmody
 
07.03.17
14:46
(33) У него это статьи доходов/расходов. Как по ним агрегацию делать?
35 Про100Филя
 
07.03.17
14:49
(0)Однозначно две таблицы. Таблица самих строк и таблица родителей. В таком случае все запросы к скулю станут простыми + легче будет запросом/добавлять удалять строки вместе с вложеными.
36 Волшебник
 
модератор
07.03.17
14:51
(34) Можно сделать 2 справочника, подчинённых друг другу, например, группы статей и сами статьи. Тогда вообще без иерархии получится. Кстати, в одной программе так и сделано (Personal Finance от SKM)
37 Asmody
 
07.03.17
14:51
(35) Да-да, "простыми". Например, выбрать все записи под родителем 1го уровня. А уровней, ну допустим, 2-3.
38 Asmody
 
07.03.17
14:52
(36) Можно операции помечать не статьями, а тегами. И собирать аналитику по тегам.
39 Asmody
 
07.03.17
14:55
Например, вы поехали куда-то с целью развлечься и купили там поесть. Тогда помечаешь расход: "путешествия", "развлечения" и "еда".
40 Волшебник
 
модератор
07.03.17
14:55
(38) Неплохая идея. И ещё надо обязательно прикрутить корованы, которые можно грабить. И котиков.
41 arsik
 
гуру
07.03.17
14:56
(39) сложно общую сумму по иерархии вывести
42 Волшебник
 
модератор
07.03.17
14:57
(39) а потом эта сумма будет учтена трижды :)
43 Asmody
 
07.03.17
14:57
(42) Да нет же! Общая сумма одна. Кто сказал, что суммы по тегам надо суммировать?
44 Про100Филя
 
07.03.17
14:59
+(36) Ты не понял меня. В таблице родителей прописаны все связи. Получим быстрый "обход" вверх/вниз по дереву.
45 Про100Филя
 
07.03.17
15:01
(44) ой это (37).
46 Живой Ископаемый
 
07.03.17
15:04
(38) Неистово плюсую
47 Живой Ископаемый
 
07.03.17
15:06
Кстати а тег может быть вида
#ГруппаЗатрат.ЭлементЗатрат
?
48 Вафель
 
07.03.17
15:06
(43) а как в такой структуре собрать общий расход разбитый по статьям/тегам?
49 arsik
 
гуру
07.03.17
15:07
(43) Это например так?
За месяц потрачено 100 рублей.
------------------
50 рублей на еду
50 на бухло
50 на развлешения
50 Вафель
 
07.03.17
15:07
такая структура удобна для ОТБОРА, но не для ГРУППИРОВКИ
51 arsik
 
гуру
07.03.17
15:08
Тэги подойдут там, где нет агрегатных функций.
52 Вафель
 
07.03.17
15:09
но ведь никто не делает отчетов:
В каких это числах я тратил деньги на развлечения?
53 arsik
 
гуру
07.03.17
15:09
(49) О! Родилась мысль.
Чем больше тегов к расходу, тем максимальнее окупились эти расходы.
54 Волшебник
 
модератор
07.03.17
15:15
(52) Очень здравый вопрос: когда это я ему дал в долг?
55 Вафель
 
07.03.17
15:17
(54) долги совсем должны учитываться по другому
56 Волшебник
 
модератор
07.03.17
15:20
(55) У меня долги - это те же кошельки с типами "Взято в долг" (кредиторская) и "Выдано в долг" (дебиторская). Пока так. Не хватает ещё поля "Дедлайн", после которого пора вызывать коллекторов или телохранителей.
57 Fragster
 
гуру
07.03.17
15:20
про nested sets было?
58 Fragster
 
гуру
07.03.17
15:21
я даже на 1с прикручивал в РС, чтобы соединения по иерархии можно было делать
59 Волшебник
 
модератор
07.03.17
15:21
(57) было в паре статей выше
60 Asmody
 
07.03.17
16:15
(49) Примерно. Например, повел я сына в Мак. Пишем: -630 руб. #еда #дети. А если это всё было в рамках похода в театр, то еще добавляем #развлечения.
Потому что, например, #развлечения — это не только билеты в театр, но и расходы на дорогу, на всякие мелкие игрушки, перекусы и т.д.
61 Asmody
 
07.03.17
16:18
Вообще, конечно, структура аналитики должна идти от цели — для чего вообще вы ведете учет личных финансов.
62 Волшебник
 
модератор
07.03.17
16:34
(61) Да-да. Самое главное, получить план-факт в виде диаграммы с отображением превышений плана.
63 Вафель
 
07.03.17
16:35
(62) а как план по тегам составлять?.
Или исходя из предыдуших преиодов? и без учета доходов?
64 Волшебник
 
модератор
07.03.17
16:36
(63) Не знаю. Теги вы придумали. У меня иерархия статей.
65 Вафель
 
07.03.17
16:37
(61) Тогда Asmody ты скажи, какова твоя цель учета?
66 Волшебник
 
модератор
07.03.17
16:43
(65) Дожить до зарплаты :)
67 Вафель
 
07.03.17
16:45
насколько я знаю есть 2 вида учета: реактивный и проактивный.
1 это: куда делись деньги (расходы по статьям).
2 - На эту штуку 5 руб и ни копейкой больше (бюджет)

Может еще какие варианты есть?
68 Волшебник
 
модератор
07.03.17
16:47
(67) Есть ещё режим "от получки до получки".

~Начало:
Пока ЕстьДеньги() Цикл
Тратим();
КонецЦикла;
ЗапуститьОбработчикОжиданияЗарплаты();
Перейти ~Начало:
69 Вафель
 
07.03.17
16:49
(68) Отсутвие учета - тоже учет? )))
70 Волшебник
 
модератор
07.03.17
16:53
(69) Отсутствие учёта — тоже стиль жизни.
71 lock19
 
07.03.17
17:05
(63) По регистрам подобных вопросов нет?
72 Torquader
 
08.03.17
18:03
(60) Зачем вообще теги ?
Теги - это связь одной сущности с другой.
Просто, если мы пишем статьи затрат, то у нас древовидная структура, и мы не можем одному действию записать несколько статей, нужно будет делить действие на части.
А тегов или связей наставить можно сколько угодно:
Например:МакДак->Еда,Развлечения, потом Смекта->МакДак,Лечение
73 Asmody
 
08.03.17
18:24
(65) Понять, во что мне обходится то или иное мероприятие. Задачи "дожить до зарплаты", слава богу, нет.
74 Asmody
 
08.03.17
18:27
(72) Ну, в общем-то, да. Это вопрос хороший — включать ли расходы на лечение в "стоимость" предприятия из-за которого пришлось лечиться.
75 Torquader
 
08.03.17
18:30
(74) Просто, связи между расходами позволят узнать много нового и интересного.
76 Волшебник
 
модератор
09.03.17
11:29
(20) Реализовано. Работает.