powered by simpleCommunicator - 2.0.35     © 2025 Programmizd 02
Форумы / SQLite [игнор отключен] [закрыт для гостей] / автоматизация связей многие-к-многим хелп!
17 сообщений из 17, страница 1 из 1
автоматизация связей многие-к-многим хелп!
    #39794131
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
доброго времени суток.
начал изучать sql для создания небольшой базы хранения объектов и их тегов (закладок)

собственно структуру придумал такую:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
PRAGMA foreign_keys=on;

CREATE TABLE tags (
id INTEGER PRIMARY KEY,
title TEXT NOT NULL,
CONSTRAINT uc_tagsTitle UNIQUE (title)
);

CREATE TABLE topic (
id INTEGER PRIMARY KEY,
path TEXT NOT NULL,
ftime TIMESTAMP,
CONSTRAINT uc_topicTitle UNIQUE (path)
);

CREATE TABLE topic_tags (
topic_id INTEGER NOT NULL,
tags_id INTEGER NOT NULL,
FOREIGN KEY (topic_id) REFERENCES topic(id) ON DELETE CASCADE,
FOREIGN KEY (tags_id) REFERENCES tags(id) ON DELETE CASCADE,
CONSTRAINT uc_TopicTags UNIQUE (topic_id, tags_id)
);

-- получаю список файлов с тегами так:
SELECT topic.path,tags.title FROM (
    topic JOIN topic_tags ON (topic.id = topic_tags.topic_id)
    ) JOIN tags ON (tags.id = topic_tags.tags_id);

-- получаю список файлов по указанному тегу так:
SELECT topic.path FROM (
    tags JOIN topic_tags ON tags.title = 'tag3' and tags.id = topic_tags.tags_id
	)    JOIN topic ON (topic.id = topic_tags.topic_id);

-- получаю список тегов для файла так:
SELECT tags.title FROM (
    topic JOIN topic_tags ON topic.path = '/docs/doc4.md' and topic.id = topic_tags.topic_id
	)     JOIN tags ON (tags.id = topic_tags.tags_id);



собственно этот код вроде работает нормально, но добавлять новые
записи довольно утомительно, поэтому был придуман способ:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
-- фейковое представление для вставки новых документов с тегами в 1 запрос:
CREATE VIEW inserter AS SELECT null as doc, null as fmtime, null as tag1, null as tag2, null as tag3;

-- триггер для представления (в него идёт запись документа с тегами):
CREATE TRIGGER tr_newdoc
INSTEAD OF INSERT ON inserter FOR EACH ROW
BEGIN
INSERT INTO topic (path, ftime) VALUES (new.doc, new.fmtime);
INSERT INTO tags (title) SELECT new.tag1 as title WHERE title NOT NULL;
INSERT INTO tags (title) SELECT new.tag2 as title WHERE title NOT NULL;
INSERT INTO tags (title) SELECT new.tag3 as title WHERE title NOT NULL;
END;

-- идея была использовать для добавления запрос в 1 строку наподобие:
-- для 3 тегов к документу
INSERT INTO inserter (doc,fmtime,tag1,tag2,tag3) VALUES ('/docs/test1.md', 7346342, 'tag1', 'tag2', 'tag3');
-- для документа без тегов:
INSERT INTO inserter (doc,fmtime) VALUES ('/docs/test2.md', 7346342);
-- для документа с тегами в упрощенном виде:
INSERT INTO inserter VALUES ('/docs/test3.md', 7346342, 'tag1','tag2','tag3');



в общем то код работает, записи добавляются, но вот сижу ломаю голову как
заполнить таблицу связей тоже автоматически, может кто подскажет что?
ато вечер, голова не бум бум, да и в sql я новичёк совсем ещё.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794132
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Код: sql
1.
INSERT INTO inserter (doc,fmtime,tag1,tag2,tag3) VALUES ('/docs/test1.md', 7346342, 'tag1', 'tag2', 'tag3');


Это нарушение первой нормальной формы, а еще есть вторая и третья нормальные формы, если эти термины ни о чем не говорят, то гугл в помощь, там букв немного, советую для начала изучить теорию реляционных СУБД.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794139
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Dima T
Код: sql
1.
INSERT INTO inserter (doc,fmtime,tag1,tag2,tag3) VALUES ('/docs/test1.md', 7346342, 'tag1', 'tag2', 'tag3');


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

прочёл честным образом статьи в википедии вплоть до 3й нормальной формы, там моя голова вскипела и я пожалуй позже почитаю.

к такому решению пришлось прийти, поскольку не смог нагуглить ни 1 способа в SQLite заполнять таблицы данных и их связующей таблицы без необходимости запоминания и последующего прописывания ID топиков и соответствующих им тегов.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794141
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pgetwinkпрочёл честным образом статьи в википедии вплоть до 3й нормальной формы, там моя голова вскипела и я пожалуй позже почитаю.
Для начала лучше поспать, а потом читать и вникать. В теории РСУБД есть правила, которые надо знать и не надо нарушать, а если нарушишь получишь трудноразрешимые проблемы. Теория работает только на правильно оформленных данных.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794212
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Из упрямства доделал всё же свой вариант сам
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
CREATE TRIGGER tr_newdoc
INSTEAD OF INSERT ON inserter FOR EACH ROW
BEGIN
INSERT INTO topic (path, ftime) SELECT NEW.doc, NEW.ftime WHERE NOT EXISTS
    (SELECT 1 FROM topic WHERE path = NEW.doc);

INSERT INTO tags (title) SELECT NEW.tag1 WHERE NEW.tag1 NOT NULL AND NOT EXISTS
    (SELECT 1 FROM tags WHERE title = NEW.tag1);
INSERT INTO topic_tags SELECT topic.id, tags.id FROM tags JOIN topic ON 
    (NEW.tag1 NOT NULL AND topic.path = NEW.doc and tags.title = NEW.tag1);

INSERT INTO tags (title) SELECT NEW.tag2 WHERE NEW.tag2 NOT NULL AND NOT EXISTS
    (SELECT 1 FROM tags WHERE title = NEW.tag2);
INSERT INTO topic_tags SELECT topic.id, tags.id FROM tags JOIN topic ON 
    (NEW.tag2 NOT NULL AND topic.path = NEW.doc and tags.title = NEW.tag2);

INSERT INTO tags (title) SELECT NEW.tag3 WHERE NEW.tag3 NOT NULL AND NOT EXISTS
    (SELECT 1 FROM tags WHERE title = NEW.tag3);
INSERT INTO topic_tags SELECT topic.id, tags.id FROM tags JOIN topic ON 
    (NEW.tag3 NOT NULL AND topic.path = NEW.doc and tags.title = NEW.tag3);
END;



и даже попользовался немного, наслаждаясь простотой добавления документов
Код: sql
1.
2.
3.
4.
INSERT INTO inserter (doc, ftime, tag1, tag2, tag3) VALUES ('~/mail/736.msg', 34534563, 'work', 'mail', 'to-do');
INSERT INTO inserter (doc, ftime, tag1, tag2, tag3) VALUES ('~/mail/534.msg', 56756342, 'to-do', 'mail', 'wife');
INSERT INTO inserter (doc, ftime, tag1, tag2, tag3) VALUES ('~/mail/456.msg', 45674345, 'work', 'mail', 'hobby');
INSERT INTO inserter (doc, ftime, tag1, tag2, tag3) VALUES ('~/mail/645.msg', 45743445, 'pic', 'cat', 'to-do');



но спустя несколько минут понял что данная форма годится только для добавления новых записей.

переписал так:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
-- фейковое представление для установки принадлежности тега к документу
CREATE VIEW docinserter AS SELECT null as path, null as ftime, null as tag;

-- триггер для добавления документов
CREATE TRIGGER tr_docinsert
INSTEAD OF INSERT ON docinserter FOR EACH ROW
BEGIN
INSERT INTO topic (path, ftime) SELECT NEW.path, NEW.ftime WHERE NOT EXISTS
    (SELECT 1 FROM topic WHERE path = NEW.path);

INSERT INTO tags (title) SELECT NEW.tag WHERE NEW.tag NOT NULL AND NOT EXISTS
    (SELECT 1 FROM tags WHERE title = NEW.tag);

INSERT INTO topic_tags SELECT topic.id, tags.id FROM tags JOIN topic ON 
    (NEW.tag NOT NULL AND topic.path = NEW.path and tags.title = NEW.tag);
END;


INSERT INTO docinserter (path, ftime, tag) VALUES ('~/mail/645.msg', 45743445, 'to-do');
INSERT INTO docinserter (path, ftime, tag) VALUES ('~/mail/645.msg', 45743445, 'work');
INSERT INTO docinserter (path, ftime, tag) VALUES ('~/mail/645.msg', 45743445, 'sql');



так что теперь у меня полностью автоматическое заполнение таблиц тегов, данных и их связей. ура. (удаление связей тоже автоматическое)

осталось только понять как получить список файлов у которых есть несколько тегов...
файлы с 1 тегом получаю так:
Код: sql
1.
2.
3.
4.
-- получаем список всех кошечек
SELECT topic.path, tags.title FROM (
    tags JOIN topic_tags ON tags.title like 'cat%' and tags.id = topic_tags.tags_id
	)    JOIN topic ON (topic.id = topic_tags.topic_id);


но неужели сложность выборки будет расти по экспоненте при увеличении числа тегов поиска? ведь прийдётся каскадно наращивать запрос как я понимаю, это ж сколько join-ов надо...

вариант
Код: sql
1.
2.
3.
SELECT topic.path, tags.title FROM (
    tags JOIN topic_tags ON tags.title in ('wife','mail') and tags.id = topic_tags.tags_id
	)    JOIN topic ON (topic.id = topic_tags.topic_id);


не подходит т.к. даст все файлы с тегами 'wife' или 'mail' а не 'wife' и 'mail'
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794274
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Всё, разобрался, уф.. как всегда сам, чёт никто новичкам не хочет кроме меня помогать, а ведь начало то оно самое и трудное.
спасибо хоть 1 чел вовремя меня поправил насчёт 1 формы, атоб так и пилил дальше.

Код: sql
1.
2.
3.
4.
-- собственно сам поиск сразу по всем тегам (условие "И")
SELECT topic.path FROM (
    tags JOIN topic_tags ON tags.title in ('to-do','home','wife') and tags.id = topic_tags.tags_id
	)    JOIN topic ON (topic.id = topic_tags.topic_id) group by topic.id having count (*) = 3;
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794384
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pgetwinkсобственно структуру придумал такую:
Тут все правильно.

pgetwink
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
-- получаю список файлов с тегами так:
SELECT topic.path,tags.title FROM (
    topic JOIN topic_tags ON (topic.id = topic_tags.topic_id)
    ) JOIN tags ON (tags.id = topic_tags.tags_id);

-- получаю список файлов по указанному тегу так:
SELECT topic.path FROM (
    tags JOIN topic_tags ON tags.title = 'tag3' and tags.id = topic_tags.tags_id
	)    JOIN topic ON (topic.id = topic_tags.topic_id);

-- получаю список тегов для файла так:
SELECT tags.title FROM (
    topic JOIN topic_tags ON topic.path = '/docs/doc4.md' and topic.id = topic_tags.topic_id
	)     JOIN tags ON (tags.id = topic_tags.tags_id);

А это сложносочиненная ересь.

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
--  список файлов с тегами:
SELECT topic.path, tags.title
FROM topic
JOIN topic_tags ON topic.id = topic_tags.topic_id

-- список файлов по указанному тегу:
SELECT topic.path
FROM topic
JOIN topic_tags ON topic.id = topic_tags.topic_id and topic_tags.title = 'tag3'

-- список тегов для файла:
SELECT topic_tags.title
FROM topic_tags
JOIN topic ON topic.id = topic_tags.topic_id and  topic.path = '/docs/doc4.md'



pgetwinkсобственно этот код вроде работает нормально, но добавлять новые
записи довольно утомительно,ну так потому и утомительно что ты не понял собственной структуры. Ты точно сам придумал эти три таблицы?

А вообще, добавление записей в эти три таблицы очень простая операция. Надо только понять что это три таблицы и они хоть и связаны, но продолжают оставаться тремя таблицами.
Просто поверь что объекты (topic) и тэги (tags) это соверешенно разные сущности. Добавляешь и изменяешь их по отдельности, а потом связываешь их (topic_tag).
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794385
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pgetwinkDima T
Код: sql
1.
INSERT INTO inserter (doc,fmtime,tag1,tag2,tag3) VALUES ('/docs/test1.md', 7346342, 'tag1', 'tag2', 'tag3');


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

прочёл честным образом статьи в википедии вплоть до 3й нормальной формы, там моя голова вскипела и я пожалуй позже почитаю.Почитай. Теория это полезно.
А чтобы мозги не вскипали - попробуй сделать это все не в базе данных, а на бумажке. Возьми тетрадку, карандаш - на трех отдельных листочках нарисуй свои три таблицы. А теперь запиши в них карандашиком свои данные. Подумай как это сделать удобнее всего при помощи карандаша. Потом переноси это в компьютер.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794406
Dima T
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pgetwinkВсё, разобрался, уф.. как всегда сам, чёт никто новичкам не хочет кроме меня помогать, а ведь начало то оно самое и трудное.
В этом форуме мало кто бывает, к тому же в выходные.

Как понимаю то, что ты изобретаешь называется модель EAV . Тут обсуждали эту тему.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794845
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
White Owlpgetwinkсобственно этот код вроде работает нормально, но добавлять новые
записи довольно утомительно,ну так потому и утомительно что ты не понял собственной структуры. Ты точно сам придумал эти три таблицы?

А вообще, добавление записей в эти три таблицы очень простая операция. Надо только понять что это три таблицы и они хоть и связаны, но продолжают оставаться тремя таблицами.
Просто поверь что объекты (topic) и тэги (tags) это соверешенно разные сущности. Добавляешь и изменяешь их по отдельности, а потом связываешь их (topic_tag).

Я прекрасно понимаю что пишу, но немного нехватает глубоких знаний по синтаксису и принципам выполнения команд (пока что).

кстати, ваш код для выборки по тегам и файлам не работает в SQLite3 к сожалению (вероятно в более развитых базах данных он и сработает, но SQLite не понимает что от неё хотят) с ёё точки зрения нет никакой связи между topic, tags и topic_tags поэтому и пришлось писать 2 JOIN

но спасибо за наводку, обязательно почитаю про модель EAV
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39794851
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
кстати, под "сложностью добавления" я имел в виду "сложность добавления 1 файла из командной строки в 1 команду с несколькими тегами" в приложении у меня изначально так записи и добавлялись сначала в topic потом в tags затем связи устанавливались.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39796636
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pgetwinkкстати, ваш код для выборки по тегам и файлам не работает в SQLite3 к сожалению (вероятно в более развитых базах данных он и сработает, но SQLite не понимает что от неё хотят) с ёё точки зрения нет никакой связи между topic, tags и topic_tags поэтому и пришлось писать 2 JOINНе работает, потому что я ошибся. У тебя три таблицы значит и join должен быть двойной - это верно. А вот вложенные выборки тебе не нужны.

В общем, вот тебе базовый запрос который можно фильтровать по любой из трех таблиц:
Код: sql
1.
2.
3.
4.
SELECT *
FROM topic_tags 
JOIN topic ON topic_tags.topic_id = topic.id    [and topic.a=b ....]
JOIN tags ON topic_tags.tags_id = tags.tags_Id  [and tags.c=d ....]
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39796823
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
White Owlpgetwinkкстати, ваш код для выборки по тегам и файлам не работает в SQLite3 к сожалению (вероятно в более развитых базах данных он и сработает, но SQLite не понимает что от неё хотят) с ёё точки зрения нет никакой связи между topic, tags и topic_tags поэтому и пришлось писать 2 JOINНе работает, потому что я ошибся. У тебя три таблицы значит и join должен быть двойной - это верно. А вот вложенные выборки тебе не нужны.

В общем, вот тебе базовый запрос который можно фильтровать по любой из трех таблиц:
Код: sql
1.
2.
3.
4.
SELECT *
FROM topic_tags 
JOIN topic ON topic_tags.topic_id = topic.id    [and topic.a=b ....]
JOIN tags ON topic_tags.tags_id = tags.tags_Id  [and tags.c=d ....]



Спасибо, но мне такой вариант не подходит, так как могут быть документы без тегов, как и теги могут быть "висячими" (оставшиеся без документов в результате удаления).

ps. Я времени даром не терял, у меня уже полностью оптимизированы запросы, выборки строятся исключительно по ID, база данных наполняется и всё работает максимально шустро, даже выборки по условию И по нескольким тегам (самый сложный запрос)
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39796832
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Стоп, я кажется понял что вы имели в виду, поправьте если неправ:
таблица связей самая большая поэтому выборка должна изначально делаться из неё потому что каждый последующий JOIN как бы это сказать... "усложняет сложность" в зависимости от размера таблицы (читал, что вроде как там умножение), другими словами умножить базу на салую таблицу а потом базу на произведение 1 и 2? так чтоли? надо будет мне потестировать как напишу парсер документов и наполню базу...

Спасибо ещё раз, если это так то вы меня очень выручили.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39796834
pgetwink
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
сорри, незнаю как исправить очепятку, "салую" читать как "малую"
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39797739
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pgetwinkСпасибо, но мне такой вариант не подходит, так как могут быть документы без тегов, как и теги могут быть "висячими" (оставшиеся без документов в результате удаления).
Документ без тегов это значит строка в topic не имеющая ни одной строки в topic_tag. И тоже самое для тегов.
А значит и искать такие topic и tag проще простого:
Код: sql
1.
2.
3.
select *
from topic
where id not in (select topic_id from topic_tag)


Достаточно задать вопрос "что я ищу?"
И не стесняйся рисовать таблички и работать с ними на бумаге - это ооочень помогает понять что ты делаешь.
...
Рейтинг: 0 / 0
автоматизация связей многие-к-многим хелп!
    #39797740
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
pgetwinkСтоп, я кажется понял что вы имели в виду, поправьте если неправ:
таблица связей самая большая поэтому выборка должна изначально делаться из неё потому что каждый последующий JOIN как бы это сказать... "усложняет сложность" в зависимости от размера таблицы (читал, что вроде как там умножение), другими словами умножить базу на салую таблицу а потом базу на произведение 1 и 2? так чтоли? надо будет мне потестировать как напишу парсер документов и наполню базу... И да и нет. Ты правильно понял что join добавляет еще одно произведение, но я показал порядок topic_tag join topic join tags просто для иллюстрации множественных связей. На самом деле ты можешь сделать и цепочку
Код: sql
1.
2.
3.
from topic
join topic_tags on topic.id=topic_tags.topic_id
join tags on tags.id=topic_tags.tag_id

И даже вообще без явных join'ов:
Код: sql
1.
2.
from topic, tags, topic_tags
where topic.id=topic_tag.topic_id and tags.id=topic_tags.tag_id


Функционально это все равнозначно и равноценно. Просто выбираешь то что тебе кажется более читабельным и легче для понимания. Но для сервера это все одно и то-же.

На самом деле очень сложно сказать какой порядок таблиц в join'ах будет более эффективным. По скорости может быть лучше брать маленькую таблицу сначала и навешивать на нее большую, может быть наоборот. Все зависит от данных и насколько эти данные покрыты индексами.
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / SQLite [игнор отключен] [закрыт для гостей] / автоматизация связей многие-к-многим хелп!
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]