Как мне реализовать этот многостоловый дизайн базы данных / ограничения, нормализованные? - PullRequest
2 голосов
/ 02 февраля 2010

У меня есть данные, которые выглядят примерно так ...

Elements
Class | Synthetic ID (pk)
A     | 2
A     | 3
B     | 4
B     | 5
C     | 6
C     | 7

Elements_Xref
ID (pk) | Synthetic ID | Real ID (fk)
.       | 2            | 77-8F         <--- A class
.       | 3            | 30-7D         <--- A class
.       | 6            | 21-2A         <--- C class
.       | 7            | 30-7D         <--- C class

Итак, у меня есть эти элементы, которым назначены синтетические идентификаторы и которые сгруппированы в классы. Но эти синтетические идентификаторы соединяются с Real ID s, которые нас действительно волнуют. Существует также ограничение, что Real ID не может повторяться в одном классе. Как я могу запечатлеть все это в одном целостном дизайне?

Я не хочу вставлять Real ID в верхний стол, потому что

  1. Это можно обнулять (бывают периоды, когда мы не знаем, каким должно быть Real ID чего-либо).
  2. Это внешний ключ для дополнительных данных.

Очевидно, что это можно сделать с помощью триггеров, действующих как ограничения, но мне интересно, можно ли это реализовать с помощью регулярных ограничений / уникальных индексов. Использование SQL Server 2005 .

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

Редактировать: Это вставка ключевых слов, но я думаю, что это связано с нормализацией.

Редактировать ^ 2: Как указано в комментариях ниже, я, кажется, подразумевал, что внешние ключи не могут быть обнуляемыми. Что ложно, они могут! Но то, что не может быть сделано, это установить уникальный индекс для полей, где повторяются NULL. Хотя уникальные индексы поддерживают значения NULL, они не могут ограничивать более одного NULL в наборе. Поскольку присвоение Real ID изначально разреженное, более чем NULL Real ID s на класс более вероятно.

Редактировать ^ 3: удален лишний столбец Elements.ID.

Редактировать ^ 4: Общие замечания. Кажется, в работе есть три основных подхода, один из которых я уже упоминал.

  1. Триггеры. Использование триггера в качестве ограничения для нарушения любых операций с данными, которые могут нарушить целостность данных.
  2. Индексируйте представление, которое объединяет таблицы. Фантастика, я не знал, что вы могли бы сделать это с помощью представлений и индексов.
  3. Создайте внешний ключ из нескольких столбцов. Не думал об этом, не знал, что это возможно. Добавьте поле Class в таблицу Xref. Создайте ограничение UNIQUE для (Class + Real ID) и ограничение внешнего ключа для (Class + Synthetic ID) обратно в таблицу Elements.

Ответы [ 7 ]

1 голос
/ 04 февраля 2010

Я бы:

  1. Создать уникальное ограничение для элементов (синтетический идентификатор, класс)
  2. Добавить столбец класса в Elements_Xref
  3. Добавить ограничение FOREIGN KEY дляТаблица Elements_Xref, ссылающаяся на (синтетический идентификатор, класс)

На данный момент мы точно знаем, что Elements_Xref.Class всегда соответствует Elements.Class.

Теперь нам нужно реализовать логику «уникальный, когда не ноль».Перейдите по ссылке и перейдите к разделу «Использование вычисляемых столбцов для реализации сложных бизнес-правил»: Индексы для вычисляемых столбцов: ускорение запросов, добавление бизнес-правил

Кроме того, вы можете создать индексированное представлениеon (Class, RealID) с WHERE RealID NOT NULL в предложении WHERE - это также обеспечит применение логики «уникальный, когда не ноль».

1 голос
/ 05 февраля 2010

Создайте индексированное представление для Elements_Xref с параметром Where Real_Id Not Null, а затем создайте уникальный индекс для этого представления

Create View Elements_Xref_View With SchemaBinding As
Select Elements.Class, Elements_Xref.Real_Id
From Elements_Xref
Inner Join Element On Elements.Synthetic_Id = Elements_Xref.Synthetic_Id
Where Real_Id Is Not Null
Go

Create Unique Clustered Index Elements_Xref_Unique_Index
On Elements_Xref_View (Class, Real_Id)
Go

Это не служит никакой другой цели, кроме моделирования уникального индекса, который обрабатывает нули правильно, т.е. нуль! = Нуль

1 голос
/ 04 февраля 2010

Вы можете

  1. Создать представление из набора результатов объединения Elements_Xref и Elements вместе в Synthetic ID

  2. добавить уникальное ограничение на class и [Real ID].В других новостях, это также, как вы делаете функциональные индексы в MSSQL, индексируя представления.

Вот некоторые sql:

CREATE VIEW unique_const_view AS
SELECT e.[Synthetic ID], e.Class, x.[Real ID]
FROM Elements AS e
JOIN [Elements_Xref] AS x
  ON e.[Synthetic ID] = x.[Synthetic ID]

CREATE UNIQUE INDEX unique_const_view_index ON unique_const_view ( Class, [Real ID] );

Теперь, очевидно, без ведомадля меня это решение не работает в Microsoft-land-place, потому что в MS SQL Server повторяющиеся нули будут нарушать УНИКАЛЬНОЕ ограничение: это противоречит спецификации SQL. Здесь обсуждается проблема .

Это обходной путь Microsoft:

create unique nonclustered index idx on dbo.DimCustomer(emailAddress)
where EmailAddress is not null;

Не уверен, что это 2005 или просто 2008.

1 голос
/ 02 февраля 2010

Комментарии до того, как вопрос был превращен в «бонусный» вопрос

То, что вы хотели бы сделать, это выразить, что объединение Elements и Elements_Xref имеет уникальное ограничение на Class и Real ID. Если у вас есть СУБД, которая поддерживает ограничения ASSERTION для SQL-92, вы можете сделать это.

AFAIK, ни одна СУБД не поддерживает их, поэтому вы застряли с использованием триггеров.

Кажется странным, что дизайн не ограничивает Real ID быть уникальным среди классов; Из обсуждения кажется, что данный Real ID может быть частью нескольких разных классов. Если бы действительный идентификатор был «уникальным, если не нулевым», то вы могли бы легче обеспечить уникальность, если бы СУБД поддерживала концепцию «уникального, если не нулевого» (большинство этого не делает; я верю, что есть такая, но она забыта который это).


Комментарии перед внесенными изменениями 2010-02-08

Вопрос исключает «заклинивание» Real_ID в верхней таблице (элементы); он не исключает включение класса в нижнюю таблицу (Elements_Xref), который затем позволяет вам создать уникальный индекс для Class и Real_ID в Elements_Xref, достигая (я полагаю) требуемого результата.

Из данных примера неясно, является ли синтетический идентификатор в таблице элементов уникальным или может ли он повторяться с разными классами (или, действительно, может ли синтетический идентификатор повторяться в одном классе). Учитывая, что столбец идентификатора (как представляется, является уникальным), а также столбец синтетического идентификатора кажется разумным предположить, что иногда синтетический идентификатор повторяется - в противном случае в таблице есть два уникальных столбца без веской причины. По большей части это не имеет значения - но это влияет на ограничение уникальности, если класс копируется в таблицу Elements_Xref. Еще одна возможность; возможно, класс вообще не нужен в таблице элементов; он должен жить только в таблице Elements_Xref. У нас недостаточно информации, чтобы сказать, возможно ли это.


Комментарии для внесенных изменений 2010-02-08

Теперь, когда таблица Элементов имеет Синтетический ID в качестве первичного ключа, все несколько проще. Есть комментарий, что информация «Класс» на самом деле является «месяцем», но я постараюсь не обращать на это внимания.

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

Мы не знаем, может ли один синтетический идентификатор сопоставляться только с одним действительным идентификатором или он может сопоставляться с несколькими значениями действительного идентификатора.

Поскольку синтетический идентификатор является первичным ключом элементов, мы знаем, что один синтетический идентификатор соответствует одному классу.

Мы не знаем, меняется ли сопоставление Synthetic ID и Real ID с течением времени (это может быть связано с тем, что класс связан с датой) и нужно ли помнить старое состояние.

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

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

Я не вижу совершенно не избыточного дизайна, который работает.

Я думаю, что таблица Elements_Xref должна содержать:

  • Синтетический ID
  • Класс
  • Настоящий ID

с (Синтетический ID, Класс) в качестве элемента внешнего ссылки, ссылающегося на Элементы, и с ограничением NOT NULL на Реальный ID, и с уникальным ограничением на (Класс, Реальный ID).

Таблица Elements_Xref содержит только те строки, для которых известен Real ID, и корректно обеспечивает требуемое ограничение уникальности.

Странный бит в том, что данные (Synthetic ID, Class) в Elements_Xref должны совпадать с теми же столбцами в Elements, даже если Synthetic ID является первичным ключом Elements.

В IBM Informix Dynamic Server вы можете добиться этого:

CREATE TABLE elements
(
    class CHAR(1) NOT NULL,
    synthetic_id SERIAL NOT NULL PRIMARY KEY,
    UNIQUE(class, synthetic_id)
);

CREATE TABLE elements_xref
(
    class CHAR(1) NOT NULL,
    synthetic_id INTEGER NOT NULL REFERENCES elements(synthetic_id),
    FOREIGN KEY (class, synthetic_id) REFERENCES elements(class, synthetic_id),
    real_id    CHAR(5) NOT NULL,
    PRIMARY KEY (class, real_id)
);
0 голосов
/ 02 февраля 2010

Создайте новую таблицу real_elements с полями Real ID, Class и Synthetic ID с первичным ключом Class, RealId и добавьте элементы, когда вы фактически добавите RealID

Это ограничивает использование реальных идентификаторов для классаи дает вам возможность сопоставить класс и реальный идентификатор с синтетическим идентификатором

Что касается реального идентификатора, являющегося внешним ключом, вы имеете в виду, что если он находится в двух классах, то данные, выделенные для него, будут одинаковыми,Если это так, добавьте еще одну таблицу с ключом Real Id.Затем этот ключ является внешним ключом для real_elements и любой другой таблицы, для которой нужен реальный идентификатор в качестве внешнего ключа

0 голосов
/ 02 февраля 2010

Я не думаю, что любая из ваших двух причин является препятствием для ввода Real ID в Elements. Если данный элемент имеет 0 или 1 Real ID с (но не более 1), он обязательно должен быть в таблице Elements. Тогда это позволит вам ограничить уникальность в пределах Class (я думаю).

Не могли бы вы остановиться на двух ваших причинах, чтобы не делать этого?

0 голосов
/ 02 февраля 2010

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

...