нуль або один до нуля або один


9

Як я моделюю відносини нуль або один до нуля або один на Sql Server найбільш природним способом?

Існує таблиця "небезпеки", яка містить перелік небезпек на сайті. Існує таблиця "Завдання" для роботи, яку потрібно виконати на сайті. Деякі завдання - виправити небезпеку, жодне завдання не може боротися з кількома небезпеками. Деякі небезпеки мають завдання їх усунути. Без небезпеки не може бути пов'язано з ними два завдання.

Нижче - найкраще, про що я міг придумати:

CREATE TABLE [dbo].[Hazard](
  [HazardId] [int] IDENTITY(1,1) NOT NULL,
  [TaskId] [int] NULL,
  [Details] [varchar](max) NULL,
 CONSTRAINT [PK_Hazard] PRIMARY KEY CLUSTERED 
(
  [HazardId] ASC
))

GO

ALTER TABLE [dbo].[Hazard]  WITH CHECK ADD  CONSTRAINT [FK_Hazard_Task] FOREIGN KEY([TaskId])
REFERENCES [dbo].[Task] ([TaskId])
GO


CREATE TABLE [dbo].[Task](
  [TaskId] [int] IDENTITY(1,1) NOT NULL,
  [HazardId] [int] NULL,
  [Details] [varchar](max) NULL,
 CONSTRAINT [PK_Task] PRIMARY KEY CLUSTERED 
(
  [TaskId] ASC
))

GO

ALTER TABLE [dbo].[Task]  WITH CHECK ADD  CONSTRAINT [FK_Task_Hazard] FOREIGN KEY([HazardId])
REFERENCES [dbo].[Hazard] ([HazardId])
GO

Ви зробили б це по-іншому? Причиною, що я не задоволений цим налаштуванням, є те, що потрібно застосувати логіку програми, щоб переконатися, що завдання та небезпеки вказують один на одного, а не на інші завдання та небезпеки, і що жодне завдання / небезпека не вказує на однакову небезпеку / завдання інше завдання / небезпека вказує на.

Чи є кращий спосіб?

введіть тут опис зображення


Чи є якась причина, що ви не могли створити унікальний індекс на TaskID на таблиці небезпеки та унікальний індекс HazardID на таблиці завдань? Це зробило б так, що ви могли мати лише одну з них у таблиці, а це я думаю, що ви намагаєтесь досягти.
mskinner

@mskinner, але вони не унікальні, багато з них можуть бути null.
Андрій Савіних

ах, готча. У цьому випадку містер Бен-Ган чудово пише, як створити це обмеження, щоб дозволити кілька нулів тут sqlmag.com/sql-server-2008/unique-constraint-multiple-nulls . Я думаю, це допоможе тобі. Дайте мені знати, якщо ні.
mskinner

На додаток до цього, існує низка проблем із використанням відфільтрованих індексів, тому, ймовірно, варто прочитати їх, якщо ви не знайомі. Ось хороший блог про це. blogs.msdn.com/b/sqlprogrammability/archive/2009/06/29/… , але для цього конкретного сценарію це може спрацювати для вас, якщо припустити, що інші проблеми не заподіюють вам надто горя.
mskinner

FWIW унікальний відфільтрований індекс може застосовувати унікальність лише до ненульових рядків, наприкладCREATE UNIQUE INDEX x ON dbo.Hazards(TaskID) WHERE TaskID IS NOT NULL;
Аарон Бертран

Відповіді:


9

Ви можете перейти до власного уявлення про асиметричну схему, видаливши один із зовнішніх ключів із поточної настройки, або, щоб зберегти симетричність, ви могли видалити обидва зовнішні ключі та ввести таблицю з'єднання з унікальним обмеженням для кожної посилання .

Отже, було б так:

CREATE TABLE dbo.Hazard
(
  HazardId int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Hazard PRIMARY KEY CLUSTERED,
  Details varchar(max) NULL
);

CREATE TABLE dbo.Task
(
  TaskId int IDENTITY(1,1) NOT NULL CONSTRAINT PK_Task PRIMARY KEY CLUSTERED,
  Details varchar(max) NULL,
);

CREATE TABLE dbo.HazardTask
(
  HazardId int NOT NULL
    CONSTRAINT FK_HazardTask_Hazard FOREIGN KEY REFERENCES dbo.Hazard (HazardId)
    CONSTRAINT UQ_HazardTask_Hazard UNIQUE,
  TaskId int NOT NULL
    CONSTRAINT FK_HazardTask_Task FOREIGN KEY REFERENCES dbo.Task (TaskId)
    CONSTRAINT UQ_HazardTask_Task UNIQUE
);

Ви можете додатково оголосити (HazardId, TaskId)основним ключем, якщо вам потрібно посилатися на ці комбінації з іншої таблиці. З метою збереження унікальних пар, однак, первинний ключ непотрібний, достатньо, щоб кожен ідентифікатор був унікальним.


1
+1 Я думаю, що це найбільш природний спосіб втілити таке відношення. але зазвичай вибираємо кортеж як основний, лише якщо він мінімальний. В кортежі (HazardId, TaskId)є кортежі (HazardId)і (TaskId)обидва однозначно ідентифікують рядок цієї таблиці. Один з них має бути обраний як основний ключ.
чудо173

2

Підсумовуючи:

  • Небезпеки мають одне або нульове завдання
  • Завдання мають одну або нульову небезпеку

Якщо таблиці Завдання та небезпеки використовуються для чогось іншого (тобто завдання та / або небезпеки пов'язані з іншими даними, а модель, яку ви нам показали, спрощена, щоб відображати лише відповідні поля), я б сказав, що ваше рішення правильне.

В іншому випадку, якщо Завдання та небезпеки існують лише для з'єднання один з одним, вам не потрібно дві таблиці; ви можете створити єдину таблицю для їх взаємозв'язку з такими полями:

ID            int, PK
TaskID        int, (filtered) unique index  
TaskDetails   varchar
HazardID      int, (filtered) unique index
HazardDetails varchar

1
Я сміливо пояснив, що це має бути відфільтрований індекс у кожному конкретному випадку (хоча, як можна було б здогадатися з опису проблеми, це може виглядати не зовсім очевидно). Будь ласка, не соромтесь додатково редагувати, якщо ви вважаєте це непотрібним або хочете передати ідею по-іншому.
Андрій М

Треба сказати, що я ще не дуже задоволений цією ідеєю. Проблема полягає в тому, що мені доведеться генерувати TaskID та HazardID вручну. Якби це був 2012 рік, можна було б використовувати послідовності для цього, але навіть тоді мені це не здавалося б правильним. Я думаю, це тому, що дві речі, завдання та небезпеки - це абсолютно різні сутності, і тому важко погодитися з ідеєю зберігання їх в одній таблиці.
Андрій М

Якщо цей дизайн обраний, для чого нам потрібні TaskIDі HazardID? У вас може бути 2 стовпчики BIT IsTask, IsHazardі обмеження, що не обидва є хибними. Тоді Hazardтаблиця - це просто перегляд: CRAETE VIEW Hazard SELECT HazardID = ID, HazardDetails, ... FROM ThisTable WHERE IsHazard = 1;і Завдання відповідно.
ypercubeᵀᴹ

@ypercube Ви б зберігали безформну річ у таблиці, а потім використовували двійкове поле, щоб повідомити, чи це завдання, чи небезпека. Це некрасиве моделювання баз даних.
dr_

@AndriyM Я розумію, і погоджуюся, що в більшості випадків ми не повинні зберігати два види різних об'єктів в одній таблиці. Однак прочитайте мій застереження: якщо Завдання та небезпеки знаходяться у взаємозв'язку 1 на 1 і існують лише для цього , то допустимо використовувати єдину таблицю.
dr_

1

Інший підхід, який, здається, ще не згадується, - це мати небезпеки та завдання використовувати один і той же простір ідентифікаторів. Якщо небезпека має завдання, вона матиме той самий ідентифікатор. Якщо завдання є небезпечним, воно матиме той самий ідентифікатор.

Для заповнення цих ідентифікаторів ви використовуєте послідовність, а не стовпці ідентичності.

Запити щодо цієї моделі даних використовуватимуть (повне) зовнішнє з'єднання для отримання своїх результатів.

Цей підхід дуже схожий на відповідь @ AndriyM, за винятком його відповіді дозволяє ідентифікатори відрізнятися, а таблиця для зберігання цих відносин.

Я не впевнений, що ви хочете використовувати такий підхід для сценарію з двох таблиць, але він працює добре, коли кількість задіяних таблиць збільшується.


Дякую за внесок, я також врахував цей підхід. Зрештою я вирішив проти цього, тому що послідовність незручно реалізувати у sql2008 та більше проблем, ніж я хотів би змиритися.
Андрій Савіних
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.