Evgenii Legotckoi
Evgenii LegotckoiҚыр. 25, 2016, 1:37 Т.Қ.

PostgreSQL жүйесінде материалдандырылған жол

Реляциялық дерекқорларда ағаш құрылымдарында ақпаратты сақтау қосымша шығындары бар тапсырма болып табылады. Мұндай қосымша шығындар сұраныстар санының көбеюі де, деректер құрылымын ұйымдастыруға қызмет ететін ақпарат көлемінің ұлғаюы да болуы мүмкін.

Ағаш құрылымдарын ұйымдастырудың жалпы тәсілдері:

Аты Сипаттама
Көршілестер тізімі Көршілестер тізімі Мәліметтер құрылымын ұйымдастыру әрбір объект негізгі объект туралы ақпаратты сақтайды, яғни кесте жолында осы нысан кірістірілген объектінің идентификаторын көрсететін қосымша өріс бар.
Кірістірілген жиын Кірістірілген жиын Кірістірілген жиындар сол және оң жақ пернелер деп аталатындар туралы ғана емес, сонымен қатар кірістіру деңгейі туралы ақпаратты сақтайды. Деректер құрылымын ұйымдастырудың бұл нұсқасын оқу оңай, бірақ өзгерту қиынырақ.
Материалданған жол Материалдандырылған жол Бұл деректер құрылымының идеясы әрбір жазба ағаштың түбір элементіне толық жолды сақтайды.

Бұл деректер құрылымдарын қарастыру сайттағы мақалаларға түсініктемелерді қолдауды енгізу қажеттілігінен туындады.


Жақындар тізімі маған түсініктемелерді ұйымдастыру үшін біршама ыңғайсыз шешім болып көрінді, өйткені негізгі нысандарды табу және ағаш құрылымын құру кезінде қиындықтар болуы мүмкін, дегенмен тәсілдің өзі элементтерді тез жоюға және қосуға мүмкіндік береді.

Кірістірілген жиын - бұл шағын сайтта пікірлерді ұйымдастырудың өте қиын әдісі, тіпті Habr сияқты үлкен ресурстарға қарасаңыз да, мақалалар астында 5000 пікір жиі бола бермейді. Мақаладағы бірінші бос түсініктеме деп аталатын, бүкіл ағаш салынатын жасырын түбір элементін қосу қажет болатындығы мені де шатастырды. Неліктен қосымша нысандарды шығару керек? - Мен қателескен шығармын. Ал, «қарсы» соңғы дәлел жаңа пікірлерді қосу кезінде кілттерді қайта есептеу қажеттілігі болды. Жалпы, бұл құрылымның артықшылықтарына қарамастан, осы сайттың аясында және оның қазіргі жағдайы - пікірлерді сақтаудың бұл тәсілі торғайларға зеңбірек отына айналады.

Бірақ Материалданған жол бізге қажет нәрсе болып көрінді. Әрбір түсініктемеде толық жол бар. Сонымен қатар мақаланың астында бірнеше түсініктеме ағаштарын ұйымдастыруға болады. Яғни, бірінші деңгейде тұрған кез келген түсініктеме автоматты түрде оның ағашының түбірі болып саналады. Ал жаңа түсініктеме қосқан кезде болашақ ата-анадан толық жолды алып, оған тек жаңа түсініктеменің идентификаторын қосу керек. Сонымен қатар, белгілі бір дерекқорға қатысты және біз PostgreSQL туралы айтып отырмыз, жолды сақтау бүтін мәндердің массиві ретінде ұсынылуы мүмкін, өйткені PostgreSQL массивтерді қолдайды (Айтпақшы, мен көбірек MySQL -ге қарағанда PostgreSQL -мен жұмыс істеу туралы оқыңыз, соғұрлым бұл маған ұнайды). Сондай-ақ, түсініктеме ағаштарын таңдаған кезде осы массивтер бойынша сұрыптауға болады, бұл ағаш құрылымын сақтай отырып, барлық түсініктемелерді хронологиялық тәртіпте автоматты түрде реттеуге мүмкіндік береді, содан кейін [Django] үлгісінде (https://evileg.com) /ru/knowledge/django/ ) бетті қалыпты циклде ешқандай рекурсиясыз жасайды, себебі барлық түсініктемелер дерекқор құралдарымен дұрыс сұрыпталады.

PostgreSQL жүйесінде қолдану

Енді бізде келесі құрылымы бар бірнеше түсініктемелері бар дерексіз бет бар деп елестетіп көрейік:

id | name
-------------------
1  | comment\_1
2  |   comment\_1\_1
3  |     comment\_1\_1\_1
4  |   comment\_1\_2
5  | comment\_2
6  |     comment\_2\_1
7  |     comment\_2\_2
8  | comment\_3

Нәтижесінде түсініктеме құрылымы келесі сұрау арқылы қалыптасады:

CREATE TABLE comments (
  id serial primary key,
  path integer[] not null,
  content varchar(200) not null
);

Бұл құрылымның бірегей автоматты өсетін идентификаторы бар, оны хронологиялық идентификатордың бір түрі ретінде де қарастыруға болады, өйткені теориялық тұрғыдан алғанда, кейінгі уақыты бар түсініктеме дерекқорда төменгі идентификаторы бар түсініктемеден бұрын пайда бола алмайды.

Дерекқорды түсініктемелермен толтырыңыз:

INSERT INTO comments (path, content) VALUES 
  ('{1}', 'comment\_1'),
  ('{1,2}', 'comment\_1\_1'),
  ('{1,2,3}', 'comment\_1\_1\_1'),
  ('{1,4}', 'comment\_1\_2'),
  ('{5}', 'comment\_2'),
  ('{5,6}', 'comment\_2\_1'),
  ('{5,7}', 'comment\_2\_2'),
  ('{8}', 'comment\_3');

Нәтижесінде біз келесі кестені аламыз:

id | path    | content       
-------------------------
1  | {1}     | comment\_1    
2  | {1,2}   | comment\_1\_1  
3  | {1,2,3} | comment\_1\_1\_1
4  | {1,4}   | comment\_1\_2
5  | {5}     | comment\_2  
6  | {5,6}   | comment\_2\_1
7  | {5,7}   | comment\_2\_2
8  | {8}     | comment\_3  

еншілес элементтерді табыңыз

Еншілес элементтерді іздеу үшін массивді қайта анықтау операторын пайдалану керек && . Бұл операторды пайдалана отырып, олардың жолында негізгі элементтің идентификаторы бар элементтерді табамыз. ID = 1 элемент үшін барлық еншілес элементтерді таңдайық.

Сонымен қатар, біз барлық элементтерді ұя салу реті бойынша сұрыптай аламыз, бұл жолдың ұзындығы.

Төмендегі сұрауда :id = 1 .

SELECT * FROM comments
WHERE path && ARRAY[:id]
ORDER BY array\_length(path, 1);

Шығару кезінде біз келесі нәтижені аламыз:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  2 | {1,2}   | comment\_1\_1
  4 | {1,4}   | comment\_1\_2
  3 | {1,2,3} | comment\_1\_1\_1
(4 rows)

Негізгі элементтерді табыңыз

Барлық негізгі элементтердің тізімін алу ағымдағы элемент жолының бөлігі болып табылатын барлық элементтерді таңдау болып табылады. Мысалы, біз ID = 3 , яғни түсініктеме_1_1_1. бар түсініктеменің негізгі элементтерінің тізімін аламыз.

Төмендегі сұрауда :id = 3.

SELECT * FROM comments
WHERE id != :id 
AND array[id] && (
  SELECT path 
  FROM comments 
  WHERE id = :id
)
ORDER BY path;

Нәтижесінде біз келесі нәтижені аламыз:

 id | path  |   content   
----+-------+-------------
  1 | {1}   | comment\_1
  2 | {1,2} | comment\_1\_1
(2 rows)

Элементтерді қосу

Жаңа жазбаны жасаған кезде, біз жаңа элементтің идентификаторы қосылатын негізгі элементтің ағымдағы жолы бойымен жолды қалыптастыруымыз керек. Негізгі элемент идентификаторы 4 болатын жаңа жазба қостық делік.

Келесі сұрауда :id = 4.

INSERT INTO comments (content, path)
values (
  'comment\_1\_2\_3', 
  (SELECT path FROM comments WHERE id = :id) || (select currval('comments\_id\_seq')::integer)
);

|| операторы негізгі элементтің жолын жаңа элементтің идентификаторымен біріктіреді. Бұл ретте кестені құру кезінде автоматты түрде жасалатын comments_id_seq тізбегінен жаңа ID мәнін алу үшін біз типті трансляциялауды орындауымыз керек, өйткені жолда Integer типті массив бар. [] , және currval biginteger түрін қайтарады.

Біз келесі кестені аламыз:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  2 | {1,2}   | comment\_1\_1
  3 | {1,2,3} | comment\_1\_1\_1
  4 | {1,4}   | comment\_1\_2
  5 | {5}     | comment\_2
  6 | {5,6}   | comment\_2\_1
  7 | {5,7}   | comment\_2\_2
  8 | {8}     | comment\_3
  9 | {1,4,9} | comment\_1\_2\_3
(9 rows)

Элементті жою

Элементті жою барлық еншілес элементтерді жою арқылы орындалады, ол сонымен қатар массивтердің қиылысуы арқылы жүзеге асырылады.

Мысалы, идентификаторы 5-ке тең элементті алып тастаймыз. ( :id = 5 )

DELETE FROM comments
WHERE path && ARRAY[:id];

Нәтижесінде кесте келесідей болады:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  2 | {1,2}   | comment\_1\_1
  3 | {1,2,3} | comment\_1\_1\_1
  4 | {1,4}   | comment\_1\_2
  8 | {8}     | comment\_3
  9 | {1,4,9} | comment\_1\_2\_3
(6 rows)

Элементті жылжыту

Соңында элементтердің бірін оның ұрпақтарымен бірге басқа элементке жылжытайық. Таңдау үшін көп нәрсе болмағандықтан, ID = 2 элементті ID 8-ге тең элементке жылжытамыз.

Жалпы, бір элементті екіншісіне жылжыту үшін осы элемент пен оның барлық еншілестері үшін ағымдағы ата-анаға сәйкес келетін жол бөлігін жаңа ата-анаға сәйкес келетін жолға ауыстыру қажет.

Жалғыз қиындық - элемент деңгейін алу үшін бізге жылжытуды бір сұранысқа айналдыруға көмектесетін сақталған процедураны жазу керек. Оны деңгей деп атаймыз және оған біз жылжытатын элементтің идентификаторын береміз.

CREATE OR REPLACE FUNCTION level(element\_id integer) RETURNS integer AS
$$
DECLARE result integer;
BEGIN
SELECT array\_length(path, 1) FROM comments WHERE comments.id = element\_id INTO result;
RETURN result;
END
$$ LANGUAGE plpgsql;

Сақталған процедура тіркелгеннен кейін біз оны деректерді тасымалдау сұрауында қолданамыз.

Бұл сұрау мыналарды қамтиды:

  • :new_parent_id - болашақ ата-ананың бірегей идентификаторы, (8-ге тең);
  • :жылжымалы_id – жылжытылатын элементтің бірегей идентификаторы (2-ге тең).
UPDATE comments
SET path = (
  SELECT path
  FROM comments
  WHERE id = :new\_parent\_id
) || path[(SELECT level(:movable\_id)) : array\_length(path, 1)]
WHERE path && ARRAY[:movable\_id];

Нәтижесінде біз келесі пішіндегі кестені аламыз:

 id |  path   |    content    
----+---------+---------------
  1 | {1}     | comment\_1
  4 | {1,4}   | comment\_1\_2
  8 | {8}     | comment\_3
  9 | {1,4,9} | comment\_1\_2\_3
  2 | {8,2}   | comment\_1\_1
  3 | {8,2,3} | comment\_1\_1\_1
(6 rows)
Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

Mitai .
  • Қаң. 5, 2020, 3:54 Т.Ж.

простите а Django MPTT в этом варианте не подходит? она вроде как раз для этих целей разрабатывалась

Evgenii Legotckoi
  • Қаң. 6, 2020, 3:11 Т.Ж.

Добрый день. Почитал описание Django MPTT, и да, подойдёт. Рассматривайте статью как базовую теорию.

Nomad
  • Қар. 2, 2020, 11:02 Т.Ж.
  • (өңделген)

Django MPTT использует для построения дерева алгоритм Nested Set

во вторых если надо создавать динамическое дерево прямо в хтмл документе, Django MPTT, не подойдет, а вернее подойдет только если вы знакомы с алгоритмом Nested Set, а это означает что при добавлении нового элемента в дерево надо перешитать полностью все значения записей по полям lft и rght имея в виду еще поля parent_id и tree_id

=====

вопрос к автору статьи, скажите а Materialized Path сохраняет ордер элементов дерева. предположим что у нас есть дерево:

comment_1
comment_1_1
comment_1_1_1
comment_1_2
comment_2
comment_2_1
comment_2_2
comment_3

мы тут видим 3 родителя: comment_1, comment_2, comment_3

и мы видим что у родителя comment_2 есть 2 чаилда: comment_2_1 и comment_2_2

вот если мы поменяем местами данные чаилды как Materialized Path сохранит это????

Evgenii Legotckoi
  • Қаң. 28, 2021, 4:06 Т.Ж.

Самый простой способ, который приходит на ум, это скопировать comment_2_1 до нового комментария comment_2_3, чтобы у него был более старший id и переписать колонку path. А оригинальный comment_2_1 удалить. Только так.

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
Г

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:66ұпай,
  • Бағалау ұпайлары-1
t

C++ - Тест 001. Первая программа и типы данных

  • Нәтиже:33ұпай,
  • Бағалау ұпайлары-10
t

Qt - Тест 001. Сигналы и слоты

  • Нәтиже:52ұпай,
  • Бағалау ұпайлары-4
Соңғы пікірлер
G
GoattRockҚыр. 3, 2024, 1:50 Т.Қ.
Linux жүйесінде файлдарды қалай көшіруге болады Задумывались когда-нибудь о том, как мы привыкли доверять свои вещи службам грузоперевозок? Сейчас такие услуги стали неотъемлемой частью нашей жизни, особенно когда речь идет о переездах между …
d
dblas5Шілде 5, 2024, 11:02 Т.Ж.
QML - Сабақ 016. SQLite деректер қоры және онымен QML Qt-та жұмыс істеу Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssrАқп. 8, 2024, 6:43 Т.Қ.
Qt Linux - Сабақ 001. Linux астында Autorun Qt қолданбасы как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
АК
Анатолий КононенкоАқп. 5, 2024, 1:50 Т.Ж.
Qt WinAPI - Сабақ 007. Qt ішінде ICMP Ping арқылы жұмыс істеу Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
Енді форумда талқылаңыз
Evgenii Legotckoi
Evgenii LegotckoiМаусым 24, 2024, 3:11 Т.Қ.
добавить qlineseries в функции Я тут. Работы оень много. Отправил его в бан.
F
FynjyШілде 22, 2024, 4:15 Т.Ж.
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …
BlinCT
BlinCTМаусым 25, 2024, 1 Т.Ж.
Нарисовать кривую в qml Всем привет. Имеется Лист листов с тосками, точки получаны интерполяцией Лагранжа. Вопрос, как этими точками нарисовать кривую? ChartView отпадает сразу, в qt6.7 появился новый элемент…
BlinCT
BlinCTМамыр 5, 2024, 5:46 Т.Ж.
Написать свой GraphsView Всем привет. В Qt есть давольно старый обьект дял работы с графиками ChartsView и есть в 6.7 новый но очень сырой и со слабым функционалом GraphsView. По этой причине я хочу написать х…
Evgenii Legotckoi
Evgenii LegotckoiМамыр 2, 2024, 2:07 Т.Қ.
Мобильное приложение на C++Qt и бэкенд к нему на Django Rest Framework Добрый день. По моему мнению - да, но то, что будет касаться вызовов к функционалу Андроида, может создать огромные трудности.

Бізді әлеуметтік желілерде бақылаңыз