2010-01-21 3 views
7

У меня есть три основных типа объектов: люди, предприятия и активы. Каждый объект может принадлежать одному и только одному Лицу или Бизнесу. Каждый человек и бизнес могут владеть от 0 до многих активов. Какова была бы наилучшая практика для хранения этого типа условных отношений в Microsoft SQL Server?Проектирование отношения условных баз данных в SQL Server

Мой первоначальный план состоит в том, чтобы в таблице «Активы» были введены два нулевых внешних ключа, один для «Люди» и «Один для бизнеса». Одно из этих значений будет равно нулю, а другое - владельцу. Проблема, которую я вижу в этой настройке, заключается в том, что для ее интерпретации и принудительной реализации требуется логика приложения. Это действительно лучшее решение или есть другие варианты?

ответ

8

Введение супертипов и подтипов

Я предлагаю вам использовать супертипы и подтипов.Во-первых, создать PartyType и Party таблицы:

CREATE TABLE dbo.PartyType (
    PartyTypeID int NOT NULL identity(1,1) CONSTRAINT PK_PartyType PRIMARY KEY CLUSTERED 
    Name varchar(32) CONSTRAINT UQ_PartyType_Name UNIQUE 
); 

INSERT dbo.PartyType VALUES ('Person'), ('Business'); 

Supertype

CREATE TABLE dbo.Party (
    PartyID int identity(1,1) NOT NULL CONSTRAINT PK_Party PRIMARY KEY CLUSTERED, 
    FullName varchar(64) NOT NULL, 
    BeginDate smalldatetime, -- DOB for people or creation date for business 
    PartyTypeID int NOT NULL 
     CONSTRAINT FK_Party_PartyTypeID FOREIGN KEY REFERENCES dbo.PartyType (PartyTypeID) 
); 

подтипы

Затем, если есть столбцы, которые являются уникальными для человека, создать Person таблицу с только те:

CREATE TABLE dbo.Person (
    PersonPartyID int NOT NULL 
     CONSTRAINT PK_Person PRIMARY KEY CLUSTERED 
     CONSTRAINT FK_Person_PersonPartyID FOREIGN KEY REFERENCES dbo.Party (PartyID) 
     ON DELETE CASCADE, 
    -- add columns unique to people 
); 

И если есть столбцы, которые являются уникальными для бизнеса, создать Business таблицу только те:

CREATE TABLE dbo.Business (
    BusinessPartyID int NOT NULL 
     CONSTRAINT PK_Business PRIMARY KEY CLUSTERED 
     CONSTRAINT FK_Business_BusinessPartyID FOREIGN KEY REFERENCES dbo.Party (PartyID) 
     ON DELETE CASCADE, 
    -- add columns unique to businesses 
); 

Использование и примечания

Наконец, ваша Asset таблица будет выглядеть примерно так:

CREATE TABLE dbo.Asset (
    AssetID int NOT NULL identity(1,1) CONSTRAINT PK_Asset PRIMARY KEY CLUSTERED, 
    PartyID int NOT NULL 
     CONSTRAINT FK_Asset_PartyID FOREIGN KEY REFERENCES dbo.Party (PartyID), 
    AssetTag varchar(64) CONSTRAINT UQ_Asset_AssetTag UNIQUE 
); 

Взаимосвязь между партийным столом супертипа с подтипом ta bles Business и Person - «один к нулю или один». Теперь, в то время как подтипы обычно не имеют соответствующей строки в другой таблице, в этой конструкции есть возможность иметь Сторону, которая заканчивается в обеих таблицах. Однако вам действительно может понравиться следующее: иногда человек и бизнес почти взаимозаменяемы. Если это не полезно, в то время как триггер для принудительного выполнения этого будет довольно легко выполнен, лучшим решением является, вероятно, добавить столбец PartyTypeID в таблицы подтипов, сделав его частью PK & FK и положив ограничение CHECK на PartyTypeID.

Красота этой модели заключается в том, что, когда вы хотите создать столбец, который имеет ограничение для бизнеса или человека, вы создаете ограничение для соответствующей таблицы вместо таблицы сторонних участников.

Кроме того, при желании может оказаться полезным включение каскадного удаления в ограничениях, а также триггер INSTEAD OF DELETE в таблицах подтипов, которые вместо этого удаляют соответствующие идентификаторы из таблицы супертипов (это не гарантирует, что строки супертипа, которые не имеют подтипа строки присутствуют). Эти запросы очень просты и работают на уровне всей строки-существующего или не существующего уровня, что, на мой взгляд, является гигантским улучшением по сравнению с любым проектом, который требует проверки согласованности столбцов.

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

ID Колонка Именование

Вопрос о том, стоит ли называть столбец в таблице Person PartyID, PersonID или PersonPartyID твой собственные предпочтения, но я думаю, что лучше назвать их PersonPartyID или BusinessPartyID - терпимости к стоимости более длинного имени, это позволяет избежать двух типов путаницы. Например, кто-то, незнакомый с базой данных, видит BusinessID и не знает, что это PartyID или видит PartyID и не знает, что он ограничен внешним ключом только в таблице Business.

Если вы хотите создать представления для таблиц Party и Business, они даже могут быть материализованные представления, так как это просто внутреннее соединение, и вы могли переименовать PersonPartyID столбец PersonID, если вы действительно были так склонны (хотя Я бы не стал). Если это имеет большое значение для вас, вы можете даже активировать триггеры INSTEAD OF INSERT и INSTEAD OF UPDATE для этих представлений, чтобы обрабатывать вставки для двух таблиц для вас, чтобы представления отображались полностью как их собственные таблицы для многих прикладных программ.

Создание Вашего Предполагаемого Проектные работы как есть

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

ALTER TABLE dbo.Assets 
ADD CONSTRAINT CK_Asset_PersonOrBusiness CHECK (
    CASE WHEN PersonID IS NULL THEN 0 ELSE 1 END 
    + CASE WHEN BusinessID IS NULL THEN 0 ELSE 1 END = 1 
); 

Однако, я не рекомендую это решение.

Заключительные мысли

Естественным третьего подтипа добавить является организация, в смысле чего-то, что люди и предприятия могут иметь членство в. Supertype и подтипа также элегантно решить клиентов/сотрудников, клиентов/поставщика, и другие проблемы, аналогичные тем, которые вы представили.

Будьте осторожны, чтобы не путать «Is-A» с «Acts-As-A». Вы можете сказать, что участник - это клиент, заглядывая в таблицу заказов или просматривая счет заказа, и может вообще не нуждаться в таблице Customer. Также не путайте идентичность с жизненным циклом: прокат автомобиля может быть в конечном итоге продан, но это прогресс в жизненном цикле, и его следует обрабатывать с данными столбцов, а не с таблицей - автомобиль не начинается как RentalCar и получить превратился в ForSaleCar позже, это автомобиль все время. Или, возможно, RentalItem, может быть, в этом бизнесе тоже будут арендовать другие вещи. Вы поняли эту идею.

Возможно, нет необходимости иметь таблицу PartyType. Тип партии может определяться наличием строки в соответствующей таблице подтипов. Это также позволило бы избежать потенциальной проблемы PartyTypeID, не соответствующей присутствию таблицы подтипов. Одна из возможных реализаций заключается в том, чтобы сохранить таблицу , но удалите PartyTypeID из таблицы Party, затем в представлении таблицы Party верните правильное значение PartyTypeID, на основании которого в таблице подтипов имеется соответствующая строка. Это не будет работать, если вы решите разрешить сторонам быть оба подтипами. Затем вы просто придерживаетесь подтипов и знаете, что то же значение BusinessID и PersonID ссылаются на эту же сторону.

Дальнейшее чтение На этой Pattern

Пожалуйста см A Universal Person and Organization Data Model для более полного и теоретической обработки.

Недавно я нашел следующие статьи полезными для описания некоторых альтернативных подходов к моделированию наследования в базе данных.Хотя специфичные для инструмента Entity Framework ОРМ Microsoft, нет никаких причин, вы не могли бы реализовать эти сами в любом развитии БД:

P.S. Я неоднократно менял свое мнение о столбце, обозначающем идентификаторы в таблицах подтипов, из-за того, что у меня больше опыта на моем поясе.

+0

Мне нужно набрать быстрее ... когда я начал свой ответ, только HLGEM опубликовал. Sigh. – ErikE

+0

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

+0

Это было очень подумал о вас! – ErikE

0

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

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

2

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

(PeopleID is null and BusinessID is not null) or (PeopleID is not null and BusinessID is null) 
+0

Мне действительно нравится ваше ограничение проверки лучше, чем у меня только для двух столбцов. Mine значительно улучшает расширение до любого количества столбцов - просто добавьте еще одно подвыражение. С вашей версией добавление другого столбца потребует изменения каждого подвыражения. Представьте, что вы переходите от 5 столбцов к 6 ... – ErikE

0

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

1

У вас может быть другое лицо, из которого человек и бизнес «расширяются». Мы называем это лицо стороной в нашем текущем проекте. У человека и бизнеса есть FK для партии (есть отношения). И у Актива также есть FK для Party (принадлежит к отношениям).

С учетом сказанного, если в будущем Asset может использоваться несколькими экземплярами, лучше создавать отношения m: n, он дает гибкость, но усложняет логику приложения и запросы немного больше.

+0

Благодарим вас за предложение этого. Это «правильно».« –

1

ErikE's answer дает хорошее объяснение того, как можно связать отношения супертипа/подтипа в таблицах, и, скорее всего, я поеду в вашей ситуации, однако на самом деле он не затрагивает вопрос (ы), который у вас есть позировал, которые также интересны, а именно:

  1. что бы наилучшая практика для хранения этого типа условного отношения в Microsoft SQL Server?
  2. ... есть ли другие варианты?

Для тех, я рекомендую this blog entry on TechTarget который имеет отрывок из отрывок из «Руководство для разработчика по моделированию данных для SQL Server, Сопроводительное SQL Server 2005 и 2008» Эрик Джонсон и Джошуа Джонсом, который адресует 3 возможных вариантов.

В итоге они:

  1. Supertype Таблица - Почти соответствует тому, что вы предложили, есть таблица с некоторыми полями, которые всегда будут нулю, когда другие будут заполнены. Хорошо, если только несколько полей не разделяются. Таким образом, в зависимости от того, как различные Бизнес и Люди вы могли бы объединить их в одну таблицу, возможно, владельцы, а затем просто введите идентификатор владельца в своей таблице активов.
  2. Подтипные таблицы - В основном противоположность тем, что представляют собой таблицы Supertype, и это то, что у вас есть сейчас. Здесь у нас есть много уникальных полей и, возможно, один или два одинаковых, поэтому мы просто повторяем поля в каждой таблице. Как вы находите, это не подходит для вашей ситуации.
  3. Таблицы супертипов и подтипов - Комбинация обоих из них, в которой соответствующие поля помещаются в одну таблицу, а уникальные в отдельных таблицах и соответствующие идентификаторы используются для объединения записи из одной таблицы в другую. Это соответствует предложенному ErikE решению, и, как уже упоминалось, это тот, который я бы одобрил.

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

+0

Я заметил ваш ответ только сейчас. Я немного озадачен описанием №3, где вы упоминаете, что «другая таблица используется для их объединения». В схеме, которую я изложил, другая таблица ". Существует одна таблица супертипов, а затем множество таблиц подтипов. Я представил таблицу' PartyType', но эта таблица не объединяет их всех, она просто дает смысл столбцу 'PartyTypeID' (и isn ' t действительно требуется, например, если код принудительно использует значение каждого «PartyTypeID» в перечислении вместо использования таблицы для записи деталей). (продолжение ...) – ErikE

+0

Способ, которым таблицы соединяются вместе, является специальным -зеро-или-р elationship, где «PartyID», «BusinessID» и «PersonId» взаимозаменяемы - объединение не требуется, чтобы выяснить их смысл, поскольку они представляют собой просто разные имена для одного и того же (явно, «BusinessID» из 42 IS - PartyID' 42). – ErikE

+0

Привет, ErikE, вы правы, третья таблица не является обязательным требованием, это то, что у меня есть в моей схеме для создания экземпляров элемента из PartyID (так что экземпляры 1, 2 и 3 могут отображаться на PartyID 42). Я проясню свой ответ, чтобы устранить эту путаницу. – RyanfaeScotland

Смежные вопросы