db design question
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
db design question
Помогите развеять сомнения..
На готовую базу работающего продукта просят добавить такую таблицу, которая будет определять зависимость ProjectTasks друг от друга. То есть хотят упорядочить какая задача должна выполняться сначала и какая потом.
Для этих целей вводят новую таблицу ProjectTaskPredecessor.
Здесь на картинке понятнее нарисовано
Колонка predecessor - это те же taskID. То есть таблица ProjectTaskPredecessor определяет какие задачи должны быть выполнены (predecessors) до того как сама задача (taskID) начнет выполняться.
Получается 2 foreign keys из этой таблицы и оба к taskID таблицы ProjectTask.
По моему мнению введение такой таблицы в схему внесет кучу проблем из-за этих двух foreign keys, ведь весь софт написан без учета этих constraints, то есть он начнет валиться то там то здесь, а projectTask - это основная таблица по которой вообще загружается само приложение для данного userProfile ...
Вопроса два:
1) Согласны ли вы со мной?
2) Если да, не подкинете еще аргументов в защиту моей точки зрения.
Спасибо,
Сабина
На готовую базу работающего продукта просят добавить такую таблицу, которая будет определять зависимость ProjectTasks друг от друга. То есть хотят упорядочить какая задача должна выполняться сначала и какая потом.
Для этих целей вводят новую таблицу ProjectTaskPredecessor.
Здесь на картинке понятнее нарисовано
Колонка predecessor - это те же taskID. То есть таблица ProjectTaskPredecessor определяет какие задачи должны быть выполнены (predecessors) до того как сама задача (taskID) начнет выполняться.
Получается 2 foreign keys из этой таблицы и оба к taskID таблицы ProjectTask.
По моему мнению введение такой таблицы в схему внесет кучу проблем из-за этих двух foreign keys, ведь весь софт написан без учета этих constraints, то есть он начнет валиться то там то здесь, а projectTask - это основная таблица по которой вообще загружается само приложение для данного userProfile ...
Вопроса два:
1) Согласны ли вы со мной?
2) Если да, не подкинете еще аргументов в защиту моей точки зрения.
Спасибо,
Сабина
-
- Уже с Приветом
- Posts: 1071
- Joined: 18 Nov 2003 22:53
- Location: MA
Потенциальные проблемы могут возникнуть, если будет зацикливание. При Insert или Uodate нужно проверять, чтобы этого зацикливания не было.
В принципе, такая технология применяется и является довольно гибкой, единственный недостаток - сложность построения запросов и долгое их выполнение. Но и достоинства на лицо - нет необходимости держать весь путь и обновлять его в случае изменений.
В принципе, такая технология применяется и является довольно гибкой, единственный недостаток - сложность построения запросов и долгое их выполнение. Но и достоинства на лицо - нет необходимости держать весь путь и обновлять его в случае изменений.
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
testuser wrote:Потенциальные проблемы могут возникнуть, если будет зацикливание. При Insert или Uodate нужно проверять, чтобы этого зацикливания не было.
В принципе, такая технология применяется и является довольно гибкой, единственный недостаток - сложность построения запросов и долгое их выполнение. Но и достоинства на лицо - нет необходимости держать весь путь и обновлять его в случае изменений.
А вот скажем такая ситуация. Эта таблица явно введена с целью определить какие-нибуль business rules, типа никто не может начать работать над проектом XX до тех пор, пока все predecessors не завершены.
А приложение работает так, что при каждом запуске, идет чтение из ProjectTask, а может быть и task re-assignment. А вдруг эти business rules будут держать таблицу?
Just a thought,
Sabina
-
- Уже с Приветом
- Posts: 1071
- Joined: 18 Nov 2003 22:53
- Location: MA
да, кстати, это все можно сделать в одной таблице, predecessor как отдельное поле.
В реляционных БД, к сожалению, нет понятия порядка, в котором идут данные, порядок определяется только значением полей. Так что вам в любом случае порядок нужно как-то определить. Это можно сделать связанным списком (как в предлагаемом решении) либо ввести дополнительное поле - порядок в проекте. Но с этим может быть проблема, когда нужно вставить запись
В реляционных БД, к сожалению, нет понятия порядка, в котором идут данные, порядок определяется только значением полей. Так что вам в любом случае порядок нужно как-то определить. Это можно сделать связанным списком (как в предлагаемом решении) либо ввести дополнительное поле - порядок в проекте. Но с этим может быть проблема, когда нужно вставить запись
-
- Уже с Приветом
- Posts: 1071
- Joined: 18 Nov 2003 22:53
- Location: MA
Sabina wrote:А вот скажем такая ситуация. Эта таблица явно введена с целью определить какие-нибуль business rules, типа никто не может начать работать над проектом XX до тех пор, пока все predecessors не завершены.
А приложение работает так, что при каждом запуске, идет чтение из ProjectTask, а может быть и task re-assignment. А вдруг эти business rules будут держать таблицу?
Just a thought,
Sabina
Ну так бизнес-рулз будут скорее всего на чтение, а не на запись, держать таблицу не должны. Т.е. в начале проверяем, можно ли работать над проектом, если да - отдаем.
У меня в одном проекте была подобная система с бухгалтерскими счетами.
Вначале мы имели корень - валюту. Потом разные счета, которые могут иметь в себе либо дерево счетов, либо отдельный счет. Работало все прекрасно, проблем не было.
-
- Уже с Приветом
- Posts: 1476
- Joined: 05 Dec 2000 10:01
- Location: Vilnius -> Bonn
testuser wrote:Sabina wrote:А вот скажем такая ситуация. Эта таблица явно введена с целью определить какие-нибуль business rules, типа никто не может начать работать над проектом XX до тех пор, пока все predecessors не завершены.
А приложение работает так, что при каждом запуске, идет чтение из ProjectTask, а может быть и task re-assignment. А вдруг эти business rules будут держать таблицу?
Just a thought,
Sabina
Ну так бизнес-рулз будут скорее всего на чтение, а не на запись, держать таблицу не должны. Т.е. в начале проверяем, можно ли работать над проектом, если да - отдаем.
У меня в одном проекте была подобная система с бухгалтерскими счетами.
Вначале мы имели корень - валюту. Потом разные счета, которые могут иметь в себе либо дерево счетов, либо отдельный счет. Работало все прекрасно, проблем не было.
Я бы сделал еще одну колонку с fk на parent id а потом
select * from task connect by prior childid = parentid start with ?
и вы получите сразу всю иерархическую цепочку задач.
Причем если нужно отслеживать на незацикливаемость и уникальность прохода по таскам - могу подарить триггер - котроый это отслеживает
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
testuser wrote:Ну так бизнес-рулз будут скорее всего на чтение, а не на запись, держать таблицу не должны. Т.е. в начале проверяем, можно ли работать над проектом, если да - отдаем.
У меня в одном проекте была подобная система с бухгалтерскими счетами.
Вначале мы имели корень - валюту. Потом разные счета, которые могут иметь в себе либо дерево счетов, либо отдельный счет. Работало все прекрасно, проблем не было.
Спасибо, развеяли мои сомнения. А то я уже начала было нервничать, что опять очередной грех на свою душу повесила. Не люблю я эти все db schema changes, особенно после нормально работающей беты. А может так оно и происходит в большинстве случаев
Сабина
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
JustMax wrote:Я бы сделал еще одну колонку с fk на parent id а потом
В смысле внутри самой таблицы Predecessor fk c taskID на predecessor?
JustMax wrote:select * from task connect by prior childid = parentid start with ?
и вы получите сразу всю иерархическую цепочку задач.
А это что за синтаксис? Не нашла я такого "connect" в Transact_SQL
JustMax wrote:Причем если нужно отслеживать на незацикливаемость и уникальность прохода по таскам - могу подарить триггер - котроый это отслеживает
Ну как я могу отказаться от такого сооблазнительного предложения
nsabinaЭТsbcglobalДОТnet
Сабина
-
- Уже с Приветом
- Posts: 1071
- Joined: 18 Nov 2003 22:53
- Location: MA
Sabina wrote:Спасибо, развеяли мои сомнения. А то я уже начала было нервничать, что опять очередной грех на свою душу повесила. Не люблю я эти все db schema changes, особенно после нормально работающей беты. А может так оно и происходит в большинстве случаев
Сабина
Рефакторинг как правило приводит к лучшему дизайну и более надежно работающему приложению. Так что переделок не нужно бояться, главное чтобы они были в правильном направлении.
-
- Уже с Приветом
- Posts: 569
- Joined: 14 Dec 2003 04:06
- Location: Львов->Киев->Торонто
testuser wrote:Рефакторинг как правило приводит к лучшему дизайну и более надежно работающему приложению. Так что переделок не нужно бояться, главное чтобы они были в правильном направлении.
Ха! Не путайте рефакторинг с изменением функционала
Обычно изменение даже одного столбца в базе приводит к болезненному перелопачиванию stored procedures
Никакой разрухи нет. (с) Проф. Преображенский.
-
- Уже с Приветом
- Posts: 525
- Joined: 01 May 2002 20:29
- Location: CT->MA->TX->UT
-
- Уже с Приветом
- Posts: 569
- Joined: 14 Dec 2003 04:06
- Location: Львов->Киев->Торонто
testuser wrote:да, кстати, это все можно сделать в одной таблице, predecessor как отдельное поле.
А это как раз может поломать приложение
А в схеме с 2-мя таблицами приложение будет продолжать работать по мере добавления функциональности
Но с точки зрения дизайна лучше конечно одна таблица
Никакой разрухи нет. (с) Проф. Преображенский.
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
Strannik223 wrote:testuser wrote:Рефакторинг как правило приводит к лучшему дизайну и более надежно работающему приложению. Так что переделок не нужно бояться, главное чтобы они были в правильном направлении.
Ха! Не путайте рефакторинг с изменением функционала
Обычно изменение даже одного столбца в базе приводит к болезненному перелопачиванию stored procedures
Вот-вот Strannik, потому и не люблю все эти "моча в голову ударила базу поменять", особенно по нескольку раз в неделю. "Сразу надо было думать" (С)
С точки зрения процедур, наверное хорошо, что это все в отдельной таблице. Чтобы если что пойдет не так, убирать легче было.
Сабина
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
Lazy44 wrote:Я бы добавил одну колонку predcessor в таблицу ProjectTask и сделал эту колонку FK на TaskID. Тогда вторая таблица ProjectTaskPredcessorстанет не нужна.Получится обычная иерархия.
Там relationship one-to-many, то есть у одной task может быть несколько predecessors. То есть тут в одну таблицу не получится.
Сабина
-
- Уже с Приветом
- Posts: 1476
- Joined: 05 Dec 2000 10:01
- Location: Vilnius -> Bonn
Sabina wrote:В смысле внутри самой таблицы Predecessor fk c taskID на predecessor?
Yep, ето работает только в том случае если у вас отношения One to Many, Many to One. Many to Many только в отдельную таблицу.
Sabina wrote:А это что за синтаксис? Не нашла я такого "connect" в Transact_SQL
Это Оракл - стандартные иерархические запросы. Я то думал что вы с Oracle работаете .В MS SQL , насколько я знаю , с этим пока тяжко.
JustMax wrote:Причем если нужно отслеживать на незацикливаемость и уникальность прохода по таскам - могу подарить триггер - котроый это отслеживает
Sabina wrote:Ну как я могу отказаться от такого сооблазнительного предложения
nsabinaЭТsbcglobalДОТnet
Он полностью основан на фишках оракла - plsql tables, иерархические запросы. Если все равно интересно как это в Оракле делаеться - в понедельник вышлю заказным письмом
-
- Уже с Приветом
- Posts: 1476
- Joined: 05 Dec 2000 10:01
- Location: Vilnius -> Bonn
Sabina wrote:Там relationship one-to-many, то есть у одной task может быть несколько predecessors. То есть тут в одну таблицу не получится.
Сабина
Если successor все равно один то просто меняем местами (т.е не ребенок хранит ссылку на родителя а родитель на дитя)
select * from task connect by prior parentid = childid start with childid = ?
[/b]
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
JustMax wrote:Это Оракл - стандартные иерархические запросы. Я то думал что вы с Oracle работаете .В MS SQL , насколько я знаю , с этим пока тяжко.
Это уж точно. "Все смешалось - кони, люди". Оракл я на учебном проекте пользую, а на работе у нас SQL server. Мне наверно надо пояснять, чтобы в заблуждение не вводить
Сабина
-
- Уже с Приветом
- Posts: 1545
- Joined: 03 Feb 1999 10:01
Sabina wrote:Lazy44 wrote:Я бы добавил одну колонку predcessor в таблицу ProjectTask и сделал эту колонку FK на TaskID. Тогда вторая таблица ProjectTaskPredcessorстанет не нужна.Получится обычная иерархия.
Там relationship one-to-many, то есть у одной task может быть несколько predecessors. То есть тут в одну таблицу не получится.
Сабина
Тогда приведенная схема работать не будет, первичным ключом все равно назначен только taskID. Надо заводить искусственный ПК для этой таблицы.
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
-
- Уже с Приветом
- Posts: 525
- Joined: 01 May 2002 20:29
- Location: CT->MA->TX->UT
Sabina wrote:Dedal wrote:Тогда приведенная схема работать не будет, первичным ключом все равно назначен только taskID. Надо заводить искусственный ПК для этой таблицы.
да, я уже ввела pairID, просто на рисунке не исправила, sorry.
Сабина
Лучше сделайте превичный ключ на таблице ProjectTaskPredcessor composite - taskid, predcessor. И оба дожны быть FK на ProjectTask (TaskId)
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
Lazy44 wrote:Лучше сделайте превичный ключ на таблице ProjectTaskPredcessor composite - taskid, predcessor. И оба дожны быть FK на ProjectTask (TaskId)
FK конечно есть, а вот PK из комбинации двух как-то не догадалась сделать. Ну да уж ладно, написала уже все и протестировала, переделывать из-за одного ключа неохота.
А чем это лучше? Память буду экономить ?
Сабина
-
- Уже с Приветом
- Posts: 5669
- Joined: 13 Oct 2000 09:01
- Location: East Bay, CA
Еще вот такой вопрос, правда не по дизайну, сорри.
Я обратила внимание, что в нашем приложении гоняют туда-сюда целые объекты, даже когда нужны всего 2-3 аттрибута.
Скажем когда я добавляла в project сервис методы для этой новой таблицы, обратила внимание, что в соседнем методе getProjectTasks(int userID, Project proj) методу передается весь проект, хотя для запроса мне нужен только projectID.
Ну то есть запрос там будет выборкой из user function, нечто вроде
Не пойму зачем передают весь проект? Не проще ли было бы сделать getProjectTasks(int userID, int projectID) ?
Это все равно, если я в метод getProjectTaskPredecessors(int projectID) буду передавать не projectID, а весь объект Project...
Сабина
Я обратила внимание, что в нашем приложении гоняют туда-сюда целые объекты, даже когда нужны всего 2-3 аттрибута.
Скажем когда я добавляла в project сервис методы для этой новой таблицы, обратила внимание, что в соседнем методе getProjectTasks(int userID, Project proj) методу передается весь проект, хотя для запроса мне нужен только projectID.
Ну то есть запрос там будет выборкой из user function, нечто вроде
SELECT pt.taskID, pt.taskName
FROM ProjectTask pt (nolock)
INNER JOIN ProjectMember pm (nolock)
ON pt.projectID = pm.projectID
WHERE pt.projectID = @projectID AND
pm.userID = @userID
Не пойму зачем передают весь проект? Не проще ли было бы сделать getProjectTasks(int userID, int projectID) ?
Это все равно, если я в метод getProjectTaskPredecessors(int projectID) буду передавать не projectID, а весь объект Project...
Сабина
-
- Уже с Приветом
- Posts: 1071
- Joined: 18 Nov 2003 22:53
- Location: MA
Sabina wrote:Ну то есть запрос там будет выборкой из user function, нечто вродеSELECT pt.taskID, pt.taskName
FROM ProjectTask pt (nolock)
INNER JOIN ProjectMember pm (nolock)
ON pt.projectID = pm.projectID
WHERE pt.projectID = @projectID AND
pm.userID = @userID
Не пойму зачем передают весь проект? Не проще ли было бы сделать getProjectTasks(int userID, int projectID) ?
Это все равно, если я в метод getProjectTaskPredecessors(int projectID) буду передавать не projectID, а весь объект Project...
Сабина
Из лени или по ошибке. Вообще лучше передавать в методы только то что нужно, тогда уже по самому вызову можно понять, что там может измениться а что не может.
Передавать весь объект стоит только если у него много полей нужных в методе, из-за чего вызов метода становится громоздким, либо если в методе нужно менять сам объект.