db design question

User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

db design question

Post by Sabina »

Помогите развеять сомнения..
На готовую базу работающего продукта просят добавить такую таблицу, которая будет определять зависимость ProjectTasks друг от друга. То есть хотят упорядочить какая задача должна выполняться сначала и какая потом.
Для этих целей вводят новую таблицу ProjectTaskPredecessor.
Здесь на картинке понятнее нарисовано
Image

Колонка predecessor - это те же taskID. То есть таблица ProjectTaskPredecessor определяет какие задачи должны быть выполнены (predecessors) до того как сама задача (taskID) начнет выполняться.
Получается 2 foreign keys из этой таблицы и оба к taskID таблицы ProjectTask.

По моему мнению введение такой таблицы в схему внесет кучу проблем из-за этих двух foreign keys, ведь весь софт написан без учета этих constraints, то есть он начнет валиться то там то здесь, а projectTask - это основная таблица по которой вообще загружается само приложение для данного userProfile ...

Вопроса два:
1) Согласны ли вы со мной?
2) Если да, не подкинете еще аргументов в защиту моей точки зрения.

Спасибо,
Сабина
testuser
Уже с Приветом
Posts: 1071
Joined: 18 Nov 2003 22:53
Location: MA

Post by testuser »

Потенциальные проблемы могут возникнуть, если будет зацикливание. При Insert или Uodate нужно проверять, чтобы этого зацикливания не было.

В принципе, такая технология применяется и является довольно гибкой, единственный недостаток - сложность построения запросов и долгое их выполнение. Но и достоинства на лицо - нет необходимости держать весь путь и обновлять его в случае изменений.
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

testuser wrote:Потенциальные проблемы могут возникнуть, если будет зацикливание. При Insert или Uodate нужно проверять, чтобы этого зацикливания не было.

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


А вот скажем такая ситуация. Эта таблица явно введена с целью определить какие-нибуль business rules, типа никто не может начать работать над проектом XX до тех пор, пока все predecessors не завершены.
А приложение работает так, что при каждом запуске, идет чтение из ProjectTask, а может быть и task re-assignment. А вдруг эти business rules будут держать таблицу?

Just a thought,
Sabina
testuser
Уже с Приветом
Posts: 1071
Joined: 18 Nov 2003 22:53
Location: MA

Post by testuser »

да, кстати, это все можно сделать в одной таблице, predecessor как отдельное поле.
В реляционных БД, к сожалению, нет понятия порядка, в котором идут данные, порядок определяется только значением полей. Так что вам в любом случае порядок нужно как-то определить. Это можно сделать связанным списком (как в предлагаемом решении) либо ввести дополнительное поле - порядок в проекте. Но с этим может быть проблема, когда нужно вставить запись
testuser
Уже с Приветом
Posts: 1071
Joined: 18 Nov 2003 22:53
Location: MA

Post by testuser »

Sabina wrote:А вот скажем такая ситуация. Эта таблица явно введена с целью определить какие-нибуль business rules, типа никто не может начать работать над проектом XX до тех пор, пока все predecessors не завершены.
А приложение работает так, что при каждом запуске, идет чтение из ProjectTask, а может быть и task re-assignment. А вдруг эти business rules будут держать таблицу?

Just a thought,
Sabina


Ну так бизнес-рулз будут скорее всего на чтение, а не на запись, держать таблицу не должны. Т.е. в начале проверяем, можно ли работать над проектом, если да - отдаем.
У меня в одном проекте была подобная система с бухгалтерскими счетами.
Вначале мы имели корень - валюту. Потом разные счета, которые могут иметь в себе либо дерево счетов, либо отдельный счет. Работало все прекрасно, проблем не было.
User avatar
JustMax
Уже с Приветом
Posts: 1476
Joined: 05 Dec 2000 10:01
Location: Vilnius -> Bonn

Post by JustMax »

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 ?

и вы получите сразу всю иерархическую цепочку задач.
Причем если нужно отслеживать на незацикливаемость и уникальность прохода по таскам - могу подарить триггер - котроый это отслеживает :)
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

testuser wrote:Ну так бизнес-рулз будут скорее всего на чтение, а не на запись, держать таблицу не должны. Т.е. в начале проверяем, можно ли работать над проектом, если да - отдаем.
У меня в одном проекте была подобная система с бухгалтерскими счетами.
Вначале мы имели корень - валюту. Потом разные счета, которые могут иметь в себе либо дерево счетов, либо отдельный счет. Работало все прекрасно, проблем не было.


Спасибо, развеяли мои сомнения. А то я уже начала было нервничать, что опять очередной грех на свою душу повесила. Не люблю я эти все db schema changes, особенно после нормально работающей беты. А может так оно и происходит в большинстве случаев :(

Сабина
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

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 :oops:

JustMax wrote:Причем если нужно отслеживать на незацикливаемость и уникальность прохода по таскам - могу подарить триггер - котроый это отслеживает :)


Ну как я могу отказаться от такого сооблазнительного предложения :lol:
nsabinaЭТsbcglobalДОТnet

Сабина
testuser
Уже с Приветом
Posts: 1071
Joined: 18 Nov 2003 22:53
Location: MA

Post by testuser »

Sabina wrote:Спасибо, развеяли мои сомнения. А то я уже начала было нервничать, что опять очередной грех на свою душу повесила. Не люблю я эти все db schema changes, особенно после нормально работающей беты. А может так оно и происходит в большинстве случаев :(

Сабина


Рефакторинг как правило приводит к лучшему дизайну и более надежно работающему приложению. Так что переделок не нужно бояться, главное чтобы они были в правильном направлении.
User avatar
Strannik223
Уже с Приветом
Posts: 569
Joined: 14 Dec 2003 04:06
Location: Львов->Киев->Торонто

Post by Strannik223 »

testuser wrote:Рефакторинг как правило приводит к лучшему дизайну и более надежно работающему приложению. Так что переделок не нужно бояться, главное чтобы они были в правильном направлении.


Ха! Не путайте рефакторинг с изменением функционала
Обычно изменение даже одного столбца в базе приводит к болезненному перелопачиванию stored procedures
Никакой разрухи нет. (с) Проф. Преображенский.
Lazy44
Уже с Приветом
Posts: 525
Joined: 01 May 2002 20:29
Location: CT->MA->TX->UT

Post by Lazy44 »

Я бы добавил одну колонку predcessor в таблицу ProjectTask и сделал эту колонку FK на TaskID. Тогда вторая таблица ProjectTaskPredcessorстанет не нужна.Получится обычная иерархия.
User avatar
Strannik223
Уже с Приветом
Posts: 569
Joined: 14 Dec 2003 04:06
Location: Львов->Киев->Торонто

Post by Strannik223 »

testuser wrote:да, кстати, это все можно сделать в одной таблице, predecessor как отдельное поле.


А это как раз может поломать приложение
А в схеме с 2-мя таблицами приложение будет продолжать работать по мере добавления функциональности
Но с точки зрения дизайна лучше конечно одна таблица
Никакой разрухи нет. (с) Проф. Преображенский.
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Strannik223 wrote:
testuser wrote:Рефакторинг как правило приводит к лучшему дизайну и более надежно работающему приложению. Так что переделок не нужно бояться, главное чтобы они были в правильном направлении.


Ха! Не путайте рефакторинг с изменением функционала
Обычно изменение даже одного столбца в базе приводит к болезненному перелопачиванию stored procedures


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

Сабина
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Lazy44 wrote:Я бы добавил одну колонку predcessor в таблицу ProjectTask и сделал эту колонку FK на TaskID. Тогда вторая таблица ProjectTaskPredcessorстанет не нужна.Получится обычная иерархия.


Там relationship one-to-many, то есть у одной task может быть несколько predecessors. То есть тут в одну таблицу не получится.

Сабина
User avatar
JustMax
Уже с Приветом
Posts: 1476
Joined: 05 Dec 2000 10:01
Location: Vilnius -> Bonn

Post by JustMax »

Sabina wrote:В смысле внутри самой таблицы Predecessor fk c taskID на predecessor?


Yep, ето работает только в том случае если у вас отношения One to Many, Many to One. Many to Many только в отдельную таблицу.

Sabina wrote:А это что за синтаксис? Не нашла я такого "connect" в Transact_SQL :oops:


Это Оракл - стандартные иерархические запросы. Я то думал что вы с Oracle работаете :oops: .В MS SQL , насколько я знаю , с этим пока тяжко.

JustMax wrote:Причем если нужно отслеживать на незацикливаемость и уникальность прохода по таскам - могу подарить триггер - котроый это отслеживает :)


Sabina wrote:Ну как я могу отказаться от такого сооблазнительного предложения :lol:
nsabinaЭТsbcglobalДОТnet


Он полностью основан на фишках оракла - plsql tables, иерархические запросы. Если все равно интересно как это в Оракле делаеться - в понедельник вышлю заказным письмом :D
User avatar
JustMax
Уже с Приветом
Posts: 1476
Joined: 05 Dec 2000 10:01
Location: Vilnius -> Bonn

Post by JustMax »

Sabina wrote:Там relationship one-to-many, то есть у одной task может быть несколько predecessors. То есть тут в одну таблицу не получится.
Сабина


Если successor все равно один то просто меняем местами (т.е не ребенок хранит ссылку на родителя а родитель на дитя)
select * from task connect by prior parentid = childid start with childid = ?
:pain1: [/b]
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

JustMax wrote:Это Оракл - стандартные иерархические запросы. Я то думал что вы с Oracle работаете :oops: .В MS SQL , насколько я знаю , с этим пока тяжко.


Это уж точно. "Все смешалось - кони, люди". Оракл я на учебном проекте пользую, а на работе у нас SQL server. Мне наверно надо пояснять, чтобы в заблуждение не вводить :oops:

Сабина
User avatar
Dedal
Уже с Приветом
Posts: 1545
Joined: 03 Feb 1999 10:01

Post by Dedal »

Sabina wrote:
Lazy44 wrote:Я бы добавил одну колонку predcessor в таблицу ProjectTask и сделал эту колонку FK на TaskID. Тогда вторая таблица ProjectTaskPredcessorстанет не нужна.Получится обычная иерархия.


Там relationship one-to-many, то есть у одной task может быть несколько predecessors. То есть тут в одну таблицу не получится.

Сабина

Тогда приведенная схема работать не будет, первичным ключом все равно назначен только taskID. Надо заводить искусственный ПК для этой таблицы.
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Dedal wrote:Тогда приведенная схема работать не будет, первичным ключом все равно назначен только taskID. Надо заводить искусственный ПК для этой таблицы.


да, я уже ввела pairID, просто на рисунке не исправила, sorry.

Сабина
Lazy44
Уже с Приветом
Posts: 525
Joined: 01 May 2002 20:29
Location: CT->MA->TX->UT

Post by Lazy44 »

Sabina wrote:
Dedal wrote:Тогда приведенная схема работать не будет, первичным ключом все равно назначен только taskID. Надо заводить искусственный ПК для этой таблицы.


да, я уже ввела pairID, просто на рисунке не исправила, sorry.

Сабина

Лучше сделайте превичный ключ на таблице ProjectTaskPredcessor composite - taskid, predcessor. И оба дожны быть FK на ProjectTask (TaskId)
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Lazy44 wrote:Лучше сделайте превичный ключ на таблице ProjectTaskPredcessor composite - taskid, predcessor. И оба дожны быть FK на ProjectTask (TaskId)


FK конечно есть, а вот PK из комбинации двух как-то не догадалась сделать. Ну да уж ладно, написала уже все и протестировала, переделывать из-за одного ключа неохота.
А чем это лучше? Память буду экономить :)?

Сабина
User avatar
Sabina
Уже с Приветом
Posts: 5669
Joined: 13 Oct 2000 09:01
Location: East Bay, CA

Post by Sabina »

Еще вот такой вопрос, правда не по дизайну, сорри.

Я обратила внимание, что в нашем приложении гоняют туда-сюда целые объекты, даже когда нужны всего 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...

Сабина
testuser
Уже с Приветом
Posts: 1071
Joined: 18 Nov 2003 22:53
Location: MA

Post by testuser »

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...

Сабина


Из лени или по ошибке. Вообще лучше передавать в методы только то что нужно, тогда уже по самому вызову можно понять, что там может измениться а что не может.
Передавать весь объект стоит только если у него много полей нужных в методе, из-за чего вызов метода становится громоздким, либо если в методе нужно менять сам объект.

Return to “Вопросы и новости IT”