không-hoặc-một đến không-hoặc-một


9

Làm cách nào để tôi mô hình mối quan hệ 0 hoặc 1 thành 0 hoặc 1 trong Sql Server theo cách tự nhiên nhất?

Có một bảng 'Nguy hiểm' liệt kê các mối nguy hiểm trên một trang web. Có một bảng 'Nhiệm vụ' cho công việc cần được thực hiện trên một trang web. Một số Nhiệm vụ là để khắc phục một mối nguy hiểm, không có nhiệm vụ nào có thể đối phó với nhiều mối nguy hiểm. Một số mối nguy hiểm có một nhiệm vụ để sửa chúng. Không có nguy hiểm có thể có hai nhiệm vụ liên quan đến chúng.

Dưới đây là cách tốt nhất tôi có thể nghĩ đến:

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

Bạn sẽ làm điều đó khác nhau? Lý do tôi không hài lòng với thiết lập này là vì cần phải có logic ứng dụng để đảm bảo rằng các nhiệm vụ và mối nguy hiểm chỉ đến nhau và không phải là nhiệm vụ và mối nguy hiểm khác và không có nhiệm vụ / nguy hiểm nào đối với cùng một nguy cơ / nhiệm vụ một nhiệm vụ / điểm nguy hiểm khác.

Có cách nào tốt hơn?

nhập mô tả hình ảnh ở đây


Có bất kỳ lý do nào bạn không thể tạo một chỉ mục duy nhất trên TaskID trên bảng Hazard và một chỉ mục duy nhất của HazardID trên bảng Nhiệm vụ không? Điều đó sẽ làm cho nó để bạn chỉ có thể có một trong số chúng trong bảng, đó là tôi nghĩ những gì bạn đang cố gắng thực hiện.
mskinner

@mskinner nhưng chúng không phải là duy nhất, nhiều trong số chúng có thể null.
Andrew Savinykh

à, gotcha Trong trường hợp đó, ông Ben-Gan đã viết rất nhiều về cách tạo ra ràng buộc đó để cho phép nhiều null ở đây sqlmag.com/sql-server-2008/unique-constraint-multipl-nulls . Tôi nghĩ rằng sẽ làm việc cho bạn. Hãy cho tôi biết nếu không.
mskinner

Như một lưu ý phụ cho điều đó, có một số vấn đề khi sử dụng các chỉ mục được lọc, vì vậy có lẽ sẽ đáng để đọc chúng nếu bạn không quen. Đây là một blog tốt về điều đó. blog.msdn.com/b/sqlprogrammability/archive/2009/06/29/ , nhưng đối với kịch bản cụ thể này, nó có thể hoạt động tốt cho bạn, giả sử các vấn đề khác không khiến bạn quá đau buồn.
mskinner

FWIW một chỉ số lọc duy nhất có thể áp dụng độc đáo chỉ đối với các hàng không được null, ví dụCREATE UNIQUE INDEX x ON dbo.Hazards(TaskID) WHERE TaskID IS NOT NULL;
Aaron Bertrand

Câu trả lời:


9

Bạn có thể thực hiện ý tưởng của riêng mình về một lược đồ bất đối xứng bằng cách xóa một trong các khóa ngoại khỏi thiết lập hiện tại hoặc để giữ mọi thứ đối xứng, bạn có thể xóa cả khóa ngoại và giới thiệu bảng giao tiếp với một ràng buộc duy nhất trên mỗi tham chiếu .

Vì vậy, nó sẽ như thế này:

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
);

Bạn cũng có thể khai báo (HazardId, TaskId)là khóa chính nếu bạn cần tham chiếu các kết hợp này từ một bảng khác. Tuy nhiên, với mục đích duy trì các cặp duy nhất, khóa chính là không cần thiết, đủ để mỗi ID là duy nhất.


1
+1 Tôi nghĩ rằng đây là cách tự nhiên nhất để thực hiện mối quan hệ như vậy. nhưng thông thường người ta chỉ chọn một tuple làm chính nếu nó là tối thiểu Bộ tuple (HazardId, TaskId)có các bộ dữ liệu (HazardId)(TaskId)cả hai đều xác định một hàng của bảng này là duy nhất. Một trong số chúng nên được chọn làm khóa chính.
phép lạ173

2

Tóm lại:

  • Các mối nguy hiểm có một hoặc không Nhiệm vụ
  • Nhiệm vụ có một hoặc không có Nguy cơ

Nếu các bảng Nhiệm vụ và Nguy hiểm được sử dụng cho mục đích khác (ví dụ: các nhiệm vụ và / hoặc các mối nguy hiểm có dữ liệu khác được liên kết và mô hình bạn hiển thị cho chúng tôi được đơn giản hóa để chỉ hiển thị các trường có liên quan) Tôi nói rằng giải pháp của bạn là chính xác.

Mặt khác, nếu Nhiệm vụ và Nguy hiểm chỉ tồn tại để được ghép với nhau, bạn không cần hai bảng; bạn có thể tạo một bảng duy nhất cho mối quan hệ của họ, với các trường sau:

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

1
Tôi đã tự do làm rõ rằng nó phải là một chỉ số được lọc trong từng trường hợp, (mặc dù nó có thể được đoán từ mô tả của vấn đề, nó có thể trông không hoàn toàn rõ ràng). Xin vui lòng chỉnh sửa thêm nếu bạn cảm thấy không cần thiết hoặc muốn truyền đạt ý tưởng theo một cách khác.
Andriy M

Tôi phải nói rằng tôi vẫn chưa thực sự hài lòng với ý tưởng này. Vấn đề là tôi phải tạo TaskID và HazardID theo cách thủ công. Nếu đây là năm 2012, có thể sử dụng trình tự cho điều đó, nhưng ngay cả khi đó nó dường như không phù hợp với tôi. Tôi đoán đó là bởi vì hai thứ, nhiệm vụ và mối nguy hiểm, là hai thực thể hoàn toàn khác nhau và vì vậy thật khó để dung hòa với ý tưởng lưu trữ chúng trong một bảng duy nhất.
Andriy M

Nếu thiết kế này được chọn, tại sao chúng ta cần TaskIDHazardID? Bạn có thể có 2 cột BIT, IsTask, IsHazardvà một hạn chế mà không được cả hai trong số đó là sai sự thật. Sau đó, Hazardbảng chỉ đơn giản là một khung nhìn: CRAETE VIEW Hazard SELECT HazardID = ID, HazardDetails, ... FROM ThisTable WHERE IsHazard = 1;và Nhiệm vụ tương ứng.
ypercubeᵀᴹ

@ypercube Bạn sẽ lưu trữ một thứ không có hình dạng trong một bảng và sau đó sử dụng trường nhị phân để cho biết đó là Nhiệm vụ hay Nguy hiểm. Đó là mô hình cơ sở dữ liệu xấu xí.
dr_

@AndriyM Tôi hiểu và tôi đồng ý rằng, trong hầu hết các trường hợp , chúng ta không nên lưu trữ hai loại thực thể khác nhau trong cùng một bảng. Tuy nhiên, hãy đọc lời cảnh báo của tôi: nếu Nhiệm vụ và Nguy cơ nằm trong mối quan hệ 1-1 và chỉ tồn tại cho điều này , thì có thể chấp nhận sử dụng một bảng duy nhất.
dr_

1

Một cách tiếp cận khác dường như chưa được đề cập là có các Nguy cơ và Nhiệm vụ sử dụng cùng một không gian ID. Nếu Hazard có Nhiệm vụ, nó sẽ có cùng ID. Nếu một Nhiệm vụ dành cho Nguy hiểm, nó sẽ có cùng ID.

Bạn sẽ sử dụng một chuỗi thay vì các cột danh tính để điền các ID này.

Các truy vấn về loại mô hình dữ liệu này sẽ sử dụng (đầy đủ) các phép nối ngoài để lấy kết quả của chúng.

Cách tiếp cận này rất giống với câu trả lời của @ AndriyM ngoại trừ câu trả lời của anh ấy cho phép ID khác nhau và một bảng để lưu trữ mối quan hệ này.

Tôi không chắc chắn bạn muốn sử dụng phương pháp này cho kịch bản hai bảng, nhưng nó hoạt động tốt khi số lượng bảng liên quan tăng lên.


Cảm ơn bạn đã đóng góp, tôi cũng đã xem xét cách tiếp cận đó. Cuối cùng tôi đã quyết định chống lại nó bởi vì trình tự rất khó thực hiện trong sql2008 và nhiều rắc rối hơn tôi sẵn sàng đưa ra.
Andrew Savinykh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.