Что бы вы сделали, чтобы избежать противоречивых данных в этой схеме базы данных? - PullRequest
1 голос
/ 05 августа 2009

Я работаю над многопользовательским веб-сайтом на базе интернет-баз данных с SQL Server 2008 / LinqToSQL / пользовательскими репозиториями в качестве DAL. Я столкнулся с проблемой нормализации, которая может привести к несогласованному состоянию базы данных, если эксплуатируется правильно, и мне интересно, как решить эту проблему.

Проблема: несколько разных компаний имеют доступ к моему веб-сайту. Они должны иметь возможность отслеживать свои проекты и клиентов на моем сайте. Некоторые (но не все) проекты должны быть назначены клиентам.

В результате получается следующая схема базы данных:

**Companies:**
ID
CompanyName

**Clients:** 
ID 
CompanyID (not nullable)
FirstName
LastName


**Projects:**
ID
CompanyID (not nullable)
ClientID (nullable)
ProjectName

Это приводит к следующим отношениям:

Companies-Clients (1:n)
Companies-Projects (1:n)
Clients-Projects(1:n)

Теперь, если пользователь злонамеренный, он может, например, вставить проект со своим собственным CompanyID, но с ClientID, принадлежащим другому пользователю, оставив базу данных в несогласованном состоянии.

Проблема возникает аналогичным образом по всей схеме базы данных, поэтому я хотел бы решить эту проблему в общем виде, если это возможно. У меня были следующие две идеи:

  • Проверка записи в базу данных, которая может привести к несоответствиям в DAL. Это было бы общим, но требует некоторых дополнительных запросов к базе данных перед выполнением обновлений и запросов на создание, поэтому это приведет к снижению производительности.

  • Создайте дополнительную таблицу для отношений клиенты-проекты и убедитесь, что отношения, созданные таким образом, являются согласованными. Это также требует некоторых дополнительных запросов выбора, но гораздо меньше, чем в первом случае. С другой стороны, он не является универсальным, поэтому в долгосрочной перспективе легче что-то упустить, особенно при добавлении большего количества таблиц / зависимостей в базу данных.

Что бы вы сделали? Есть ли лучшее решение, которое я пропустил?

Редактировать: Вы можете удивиться, почему в таблице Projects указан CompanyID. Это потому, что я хочу, чтобы пользователи могли добавлять проекты с клиентами и без клиентов. Мне нужно отслеживать, к какой компании (и, следовательно, к какому пользователю сайта) относится безлимитный проект, поэтому для проекта нужен идентификатор компании.

Ответы [ 5 ]

2 голосов
/ 05 августа 2009

Я бы пошел с последним, имея одну или несколько таблиц, которые определяют допустимые отношения между сущностями.

1 голос
/ 05 августа 2009

Обратите внимание, что в ваших ссылках нет округлости, поэтому название вводит в заблуждение.

У вас есть возможность противоречивых данных, это другое.


Почему у вас есть "CompanyID" в таблице проекта? Идентификатор вовлеченной компании неявно дается клиентом, на которого вы ссылаетесь. Вам это не нужно.

Удалите этот столбец, и вы удалили свою проблему.

Кроме того, какова цель столбца «имя» в таблице клиента? У вас может быть клиент с одним именем, отличным от названия компании?

Или "клиент" человек в этой компании?


Редактировать : Хорошо, с разъяснением о проектах без компаний, я бы выделил ссылки, но вы не собираетесь избавляться от описываемой проблемы без ограничений, которые не позволяют использовать несколько ссылок сделал.

Простым ограничением для существующих таблиц может быть то, что поля CompanyID и ClientID строки проекта не могут быть ненулевыми одновременно.

0 голосов
/ 07 августа 2009

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

Во-первых, подход работает только потому, что единственная таблица, на которую другие таблицы ссылаются по нескольким путям, - это таблица Companies. Так как в моей базе данных это так, мне нужно только проверить, все ли ссылочные сущности n: 1 каждой сущности, которая должна быть создана / обновлена ​​/ удалена, ссылаются на одну и ту же компанию (или вообще нет компании).

Я применяю это, выводя все мои сущности Linq из одного из следующих типов:

  1. SingleReferenceEntityBase - норма. Только проверяет (через отражение), действительно ли существует только одна ссылка (независимо от того, является ли она транзитивной или непереходной) к таблице компаний. В этом случае ссылки на таблицу компаний не могут стать противоречивыми.

  2. MultiReferenceEntityBase - для особых случаев, таких как таблица Projects выше. Запрашивает все объекты, на которые имеются прямые ссылки, на какой идентификатор компании они ссылаются. Вызывает исключение, если есть несоответствие. Это стоит мне нескольких запросов на выборку для каждой операции CRUD, но, поскольку MultiReferenceEntities гораздо реже, чем SingleReferenceEntities, это незначительно.

Оба эти типа реализуют «CheckReferences», и я вызываю его всякий раз, когда сущность linq записывается в базу данных, путем частичной реализации метода OnValidate (действие System.Data.Linq.ChangeAction), который автоматически генерируется для всех сущностей Linq. ,

0 голосов
/ 06 августа 2009

Моей первой мыслью было бы создать специальную запись клиента для каждой компании с именем «Нет клиента». Затем удалите CompanyId из таблицы Project, и, если у проекта нет клиента, используйте запись «No client» вместо «обычной» записи клиента. Если обработка такого отсутствия клиента является особенной, добавьте флаг к записи отсутствия клиента, чтобы явно идентифицировать ее. (Я бы не хотел полагаться на то, что имя «Нет клиента» или что-то в этом роде - слишком размытое.)

Тогда не будет возможности хранить противоречивые данные, поэтому проблема исчезнет.

0 голосов
/ 05 августа 2009

Если вы хотите использовать такую ​​таблицу и избегать всех новых запросов, просто поместите триггеры в таблицу, а когда пользователь пытается вставить строку с неверными данными, триггер остановит его. С уважением, Йордан

...