Відповідно до Вашого опису ділового середовища, що розглядається, існує структура підтипу супертипу, яка охоплює предмет - супертип - та кожну його категорію , тобто автомобіль , човен і літак (разом з ще двома, про які не було відомо) - підтипи—.
Я детальніше опишу нижче метод, яким я б керувався, щоб керувати таким сценарієм.
Правила бізнесу
Для того, щоб почати розмежувати відповідну концептуальну схему, деякі найважливіші ділові правила, визначені до цих пір (обмеження аналізу лише до трьох розкритих категорій , щоб вони були максимально стислими), можна сформулювати таким чином:
- Користувач має нульовий один або багато- товари
- Пункт належить точно-один користувач в певний момент часу
- Один предмет може бути власником одного для багатьох користувачів у різні моменти часу
- Пункт класифікується рівно однієї категорії
- Пункт є, в усі часи,
- або Автомобіль
- або човен
- або Літак
Ілюстративна діаграма IDEF1X
На малюнку 1 представлена діаграма IDEF1X 1, яку я створив, щоб згрупувати попередні рецептури разом з іншими правилами бізнесу, які видаються відповідними:
Супертип
З одного боку, Item , супертип, представляє властивості † або атрибути, спільні для всіх Категорій , тобто
- CategoryCode - визначений як ІНОЗЕМНИЙ КЛЮЧ (FK), що посилається на Category.CategoryCode і функціонує як дискримінатор підтипу , тобто вказує точну категорію підтипу, з якою повинен бути пов'язаний даний елемент -,
- OwnerId - відрізняється як FK, який вказує на User.UserId , але я призначив йому ім'я 2 ролі , щоб точніше відобразити його особливі наслідки -,
- Foo ,
- Бар ,
- Баз і
- CreatedDateTime .
Підтипи
З іншого боку, властивості ‡, що стосуються кожної конкретної категорії , тобто
- Qux і Corge ;
- Граул , Гарплі і Плуг ;
- Xyzzy , Thud , Wibble та Flob ;
відображаються у відповідному полі підтипу.
Ідентифікатори
Потім, Item.ItemId PRIMARY KEY (PK) перемістив 3 до підтипів з різними іменами ролей, тобто,
- CarId ,
- BoatId і
- PlaneId .
Взаємовиключні асоціації
Як зображено, існує зв'язок або зв'язок кардинальності один на один (1: 1) між (a) кожним появою супертипу та (b) його додатковим підтипом.
Ексклюзивний підтип символ зображує той факт , що підтипи є взаємовиключними, тобто конкретний Item явище може бути доповнений тільки одним екземпляром підтипу: або один автомобіль , або один Plane , або одна човен (ніколи на два або більше).
† , ‡ Я використав класичні назви заповнювачів, щоб визначити деякі властивості типу сутності, оскільки їхні фактичні номінали не були вказані у питанні.
Макет рівня логічного рівня експозитарію
Отже, для обговорення логічного дизайну експозиторії я вивів наступні оператори SQL-DDL на основі діаграми IDEF1X, відображеної та описаної вище:
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient INDEX strategies based on the exact
-- data manipulation tendencies of your business context.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE UserProfile (
UserId INT NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
BirthDate DATE NOT NULL,
GenderCode CHAR(3) NOT NULL,
Username CHAR(20) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- ALTERNATE KEY.
);
CREATE TABLE Category (
CategoryCode CHAR(1) NOT NULL, -- Meant to contain meaningful, short and stable values, e.g.; 'C' for 'Car'; 'B' for 'Boat'; 'P' for 'Plane'.
Name CHAR(30) NOT NULL,
--
CONSTRAINT Category_PK PRIMARY KEY (CategoryCode),
CONSTRAINT Category_AK UNIQUE (Name) -- ALTERNATE KEY.
);
CREATE TABLE Item ( -- Stands for the supertype.
ItemId INT NOT NULL,
OwnerId INT NOT NULL,
CategoryCode CHAR(1) NOT NULL, -- Denotes the subtype discriminator.
Foo CHAR(30) NOT NULL,
Bar CHAR(30) NOT NULL,
Baz CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Item_PK PRIMARY KEY (ItemId),
CONSTRAINT Item_to_Category_FK FOREIGN KEY (CategoryCode)
REFERENCES Category (CategoryCode),
CONSTRAINT Item_to_User_FK FOREIGN KEY (OwnerId)
REFERENCES UserProfile (UserId)
);
CREATE TABLE Car ( -- Represents one of the subtypes.
CarId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Qux CHAR(30) NOT NULL,
Corge CHAR(30) NOT NULL,
--
CONSTRAINT Car_PK PRIMARY KEY (CarId),
CONSTRAINT Car_to_Item_FK FOREIGN KEY (CarId)
REFERENCES Item (ItemId)
);
CREATE TABLE Boat ( -- Stands for one of the subtypes.
BoatId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Grault CHAR(30) NOT NULL,
Garply CHAR(30) NOT NULL,
Plugh CHAR(30) NOT NULL,
--
CONSTRAINT Boat_PK PRIMARY KEY (BoatId),
CONSTRAINT Boat_to_Item_FK FOREIGN KEY (BoatId)
REFERENCES Item (ItemId)
);
CREATE TABLE Plane ( -- Denotes one of the subtypes.
PlaneId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Xyzzy CHAR(30) NOT NULL,
Thud CHAR(30) NOT NULL,
Wibble CHAR(30) NOT NULL,
Flob CHAR(30) NOT NULL,
--
CONSTRAINT Plane_PK PRIMARY KEY (PlaneId),
CONSTRAINT Plane_to_Item_PK FOREIGN KEY (PlaneId)
REFERENCES Item (ItemId)
);
Як було показано, тип похідності та кожен з типів суб'єктів представлені відповідною базовою таблицею.
Стовпці CarId
, BoatId
і PlaneId
, стримуються як ПКС відповідних таблиць, допомога в поданні концептуального рівня один-до-одного асоціації шляхом обмеження FK § , що вказує на ItemId
колонку, яка туги як PK в Item
таблиці. Це означає, що у фактичній «парі» і рядки супертипу, і рядки підтипу ідентифікуються тим самим значенням PK; таким чином, згадати це більш ніж доречно
- (А) прикріплення додаткової колонки , щоб тримати під контролем система-сурогатних значення ‖ до (б) таблиць , які стоять для підтипів (с) повністю зайвим .
§ Для запобігання проблем та помилок, що стосуються (особливо ЗОВНІШНЬОГО) визначень обмежень КЛЮЧ - ситуації, про які ви згадували в коментарях, дуже важливо враховувати залежність існування, що має місце серед різних таблиць, як це пояснюється в порядок декларування таблиць у структурі DDL експозиторії, який я також надав у цьому SQL Fiddle .
‖ Наприклад, додавши додатковий стовпець із властивістю AUTO_INCREMENT до таблиці бази даних, побудованої на MySQL.
Цілісність та послідовність
Важливо зазначити, що у вашому бізнес-середовищі ви повинні (1) забезпечити, щоб кожен рядок "супертипу" постійно доповнювався відповідним аналогом "підтипу", і, у свою чергу, (2) гарантував, що зазначене Рядок "підтип" сумісний зі значенням, що міститься в стовпці "дискримінатор" рядка "супертип".
Наскільки було б дуже елегантно застосовувати такі обставини декларативно , але, на жаль, жодна з основних платформ SQL не забезпечила належних механізмів для цього, наскільки я знаю. Тому вдаючись до процесуального кодексу в рамках КИСЛОТНИХ ПЕРЕВАГІВ, це досить зручно, тому що ці умови завжди виконуються у вашій базі даних. Іншим варіантом може бути використання TRIGGERS, але вони, як правило, роблять речі неохайними.
Оголошення корисних поглядів
Маючи логічний дизайн, як описаний вище, було б дуже практично створити один або кілька представлень, тобто похідних таблиць, що містять стовпці, що належать до двох або більше відповідних базових таблиць. Таким чином, ви можете, наприклад, ВИБІРАТИ безпосередньо З цих поглядів, не записуючи всі ПРИЄДНАННЯ щоразу, коли вам потрібно отримати "комбіновану" інформацію.
Зразок даних
У цьому відношенні скажемо, що базові таблиці "заповнені" типовими даними, наведеними нижче:
--
INSERT INTO UserProfile
(UserId, FirstName, LastName, BirthDate, GenderCode, Username, CreatedDateTime)
VALUES
(1, 'Edgar', 'Codd', '1923-08-19', 'M', 'ted.codd', CURDATE()),
(2, 'Michelangelo', 'Buonarroti', '1475-03-06', 'M', 'michelangelo', CURDATE()),
(3, 'Diego', 'Velázquez', '1599-06-06', 'M', 'd.velazquez', CURDATE());
INSERT INTO Category
(CategoryCode, Name)
VALUES
('C', 'Car'), ('B', 'Boat'), ('P', 'Plane');
-- 1. ‘Full’ Car INSERTion
-- 1.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(1, 1, 'C', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 1.2
INSERT INTO Car
(CarId, Qux, Corge)
VALUES
(1, 'Fantastic Car', 'Powerful engine pre-update!');
-- 2. ‘Full’ Boat INSERTion
-- 2.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(2, 2, 'B', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 2.2
INSERT INTO Boat
(BoatId, Grault, Garply, Plugh)
VALUES
(2, 'Excellent boat', 'Use it to sail', 'Everyday!');
-- 3 ‘Full’ Plane INSERTion
-- 3.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(3, 3, 'P', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 3.2
INSERT INTO Plane
(PlaneId, Xyzzy, Thud, Wibble, Flob)
VALUES
(3, 'Extraordinary plane', 'Traverses the sky', 'Free', 'Like a bird!');
--
Потім, вигідне думка , яке збирає один з стовпців Item
, Car
і UserProfile
:
--
CREATE VIEW CarAndOwner AS
SELECT C.CarId,
I.Foo,
I.Bar,
I.Baz,
C.Qux,
C.Corge,
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Car C
ON C.CarId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
Звичайно, можна дотримуватися аналогічного підходу, щоб ви могли також ВИБІРАТИ «повну» Boat
та Plane
інформацію прямо з однієї єдиної таблиці (похідної в цих випадках).
Після цього -Якщо ви не заперечуєте про наявність NULL знаків в результаті sets- з наступним визначенням VIEW, ви можете, наприклад, «збирати» стовпці з таблиць Item
, Car
, Boat
, Plane
і UserProfile
:
--
CREATE VIEW FullItemAndOwner AS
SELECT I.ItemId,
I.Foo, -- Common to all Categories.
I.Bar, -- Common to all Categories.
I.Baz, -- Common to all Categories.
IC.Name AS Category,
C.Qux, -- Applies to Cars only.
C.Corge, -- Applies to Cars only.
--
B.Grault, -- Applies to Boats only.
B.Garply, -- Applies to Boats only.
B.Plugh, -- Applies to Boats only.
--
P.Xyzzy, -- Applies to Planes only.
P.Thud, -- Applies to Planes only.
P.Wibble, -- Applies to Planes only.
P.Flob, -- Applies to Planes only.
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Category IC
ON I.CategoryCode = IC.CategoryCode
LEFT JOIN Car C
ON C.CarId = I.ItemId
LEFT JOIN Boat B
ON B.BoatId = I.ItemId
LEFT JOIN Plane P
ON P.PlaneId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
Код наведених тут поглядів є лише ілюстративним. Звичайно, виконання тестових вправ та модифікацій може допомогти прискорити (фізичне) виконання запитів. Крім того, вам може знадобитися видалити або додати стовпці до зазначених представлень, як диктує бізнес.
Зразкові дані та всі визначення перегляду включені в цю скрипку SQL, щоб їх можна було спостерігати «в дії».
Маніпулювання даними: Код програми та псевдоніми колон та програм
Використання прикладних програм (кодів) програми (якщо саме так ви маєте на увазі "специфічний код на стороні сервера") та псевдоніми стовпців - це інші важливі моменти, які ви зібрали в наступних коментарях:
Мені вдалося вирішити проблему [JOIN] із специфічним кодом на сервері, але я дійсно не хочу цього робити, і додавання псевдонімів до всіх стовпців може бути "наголос".
Дуже добре пояснено, дуже дякую. Однак, як я підозрював, мені доведеться маніпулювати набором результатів, коли перераховувати всі дані через схожість з деякими стовпцями, оскільки я не хочу використовувати декілька псевдонімів, щоб зберегти операцію чистішим.
Доречно зазначити, що при використанні програмного коду дуже підходящий ресурс для обробки презентаційних (або графічних) особливостей наборів результатів, уникання пошуку даних на основі рядків за рядком є першорядним для запобігання проблем швидкості виконання. Метою повинно бути "отримання" відповідних наборів даних у тото за допомогою надійних інструментів маніпулювання даними, що надаються (точно) встановленим механізмом платформи SQL, щоб ви могли оптимізувати поведінку вашої системи.
Крім того, використання псевдонімів для перейменування одного або декількох стовпців у певному обсязі може здатися наголосом, але особисто я бачу такий ресурс як дуже потужний інструмент, який допомагає (i) контекстуалізувати та (ii) розмежувати значення та намір, приписаний відповідним стовпчики; отже, це аспект, який слід ретельно обдумати щодо маніпулювання цікавими даними.
Подібні сценарії
Ви можете також допомогти цій серії публікацій і цій групі публікацій, які містять мою думку щодо двох інших випадків, що включають асоціації підтипу підтипу з взаємовиключними підтипами.
Я також запропонував рішення для бізнес-середовища, що включає кластер супертипу і підтипу, коли підтипи не виключають взаємно в цій (новій) відповіді .
Кінцеві замітки
1 Визначення інтеграції для інформаційного моделювання ( IDEF1X ) - це дуже рекомендована методика моделювання даних, яка була встановлена як стандарт в грудні 1993 р. Національним інститутом стандартів і технологій США (NIST). Воно міцно засноване на (а) деякі з теоретичних робітавтором якого є єдиним оригинатора в реляційної моделі , тобто д - р Ф. Кодда ; (b) погляд на відносини між сутністю , розроблений доктором П.П. Ченом ; а також (c) техніку проектування логічної бази даних, створену Робертом Г. Брауном.
2 У IDEF1X назва ролі - це відмітна мітка, присвоєна властивості (або атрибуту) ФК, щоб виразити значення, яке вона містить у межах відповідного типу сутності.
3 Стандарт IDEF1X визначає міграцію ключів як "Процес моделювання розміщення первинного ключа батьківського або родового об'єкта в його дочірній або категорійній сутності як іноземний ключ".
Item
таблиця міститьCategoryCode
стовпчик. Як згадувалося в розділі «Міркування про цілісність та послідовність»: