zero-or-one ถึง zero-or-one


9

ฉันจะสร้างความสัมพันธ์แบบ zero-or-one ถึง zero-or-one ใน 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 บนตาราง Hazard และดัชนีเฉพาะของ HazardID ในตารางงานได้หรือไม่ นั่นจะทำให้คุณสามารถมีหนึ่งในนั้นเท่านั้นในตารางซึ่งฉันคิดว่าคุณกำลังพยายามทำให้สำเร็จ
mskinner

@mskinner nullแต่พวกเขาจะไม่ซ้ำกันมากของพวกเขาสามารถ
Andrew Savinykh

อ้า gotcha ในกรณีที่นายเบนกานมีการเขียนที่ดีขึ้นเกี่ยวกับวิธีการสร้างข้อ จำกัด ที่จะอนุญาตให้ nulls หลายนี่ 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;
Aaron Bertrand

คำตอบ:


9

คุณสามารถไปกับความคิดของคุณเองเกี่ยวกับ schema แบบอสมมาตรโดยการลบหนึ่งในกุญแจต่างประเทศจากการตั้งค่าปัจจุบันหรือเพื่อรักษาสิ่งต่าง ๆ ให้สมมาตรคุณสามารถลบกุญแจต่างประเทศทั้งสองและแนะนำตารางแยกที่มีข้อ จำกัด ที่ไม่ซ้ำกัน .

ดังนั้นมันจะเป็นเช่นนี้:

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)เป็นคีย์หลักเพิ่มเติมได้หากคุณต้องการอ้างอิงชุดค่าผสมเหล่านี้จากตารางอื่น เพื่อวัตถุประสงค์ในการรักษาคู่ที่ไม่ซ้ำกัน แต่คีย์หลักไม่จำเป็นก็เพียงพอที่แต่ละ ID จะไม่ซ้ำกัน


1
+1 ฉันคิดว่านี่เป็นวิธีที่เป็นธรรมชาติที่สุดในการใช้ความสัมพันธ์ดังกล่าว แต่ปกติหนึ่งคนเลือก tuple เป็น primary เท่านั้นหากมีน้อยที่สุด tuple (HazardId, TaskId)มี tuples (HazardId)และ(TaskId)ทั้งคู่ระบุแถวของตารางนี้โดยเฉพาะ หนึ่งในนั้นควรเลือกเป็นคีย์หลัก
miracle173

2

เพื่อสรุป:

  • อันตรายมีงานหนึ่งงานหรือศูนย์
  • งานมีอันตรายหนึ่งหรือศูนย์

หากมีการใช้ตารางงานและอันตรายสำหรับสิ่งอื่น (เช่นงานและ / หรืออันตรายมีข้อมูลอื่นที่เกี่ยวข้องและโมเดลที่คุณแสดงให้เราเห็นนั้นง่ายต่อการแสดงเฉพาะฟิลด์ที่เกี่ยวข้อง) ฉันบอกว่าโซลูชันของคุณถูกต้อง

มิฉะนั้นถ้ามีเพียงงานและอันตรายเท่านั้นที่จะเชื่อมโยงเข้าด้วยกันคุณไม่จำเป็นต้องใช้สองตาราง คุณสามารถสร้างตารางเดียวสำหรับความสัมพันธ์ของพวกเขาด้วยฟิลด์ต่อไปนี้:

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

1
ฉันใช้เสรีภาพในการชี้แจงว่าควรเป็นดัชนีที่มีการกรองในแต่ละกรณี (แม้ว่าจะสามารถเดาได้จากคำอธิบายปัญหา แต่ก็อาจดูไม่ชัดเจนเลย) โปรดอย่าลังเลที่จะแก้ไขเพิ่มเติมหากคุณรู้สึกว่าไม่จำเป็นหรือต้องการถ่ายทอดความคิดด้วยวิธีอื่น
Andriy M

ฉันต้องบอกว่าฉันยังไม่พอใจกับความคิดนี้จริงๆ ปัญหาคือฉันต้องสร้าง TaskID และ HazardID ด้วยตนเอง ถ้านี่คือปี 2012 มันจะเป็นไปได้ที่จะใช้ลำดับสำหรับสิ่งนั้น แต่ถึงอย่างนั้นมันก็ดูเหมือนจะไม่ถูกต้องสำหรับฉัน ฉันเดาว่าเป็นเพราะทั้งสองสิ่งงานและอันตรายต่างกันอย่างสิ้นเชิงและมันจึงยากที่จะกระทบยอดกับแนวคิดในการจัดเก็บไว้ในตารางเดียว
Andriy M

หากการออกแบบนี้ถูกเลือกทำไมเราต้องTaskIDและHazardID? คุณอาจมี 2 คอลัมน์บิตIsTask, IsHazardและข้อ จำกัด ที่ไม่ทั้งสองของพวกเขาเป็นเท็จ จากนั้นHazardตารางเป็นเพียงมุมมอง: CRAETE VIEW Hazard SELECT HazardID = ID, HazardDetails, ... FROM ThisTable WHERE IsHazard = 1;และงานตามลำดับ
ypercubeᵀᴹ

@ypercube คุณต้องการจัดเก็บสิ่งที่ไม่มีรูปแบบไว้ในตารางแล้วใช้เขตข้อมูลไบนารีเพื่อบอกว่าเป็นงานหรืออันตราย มันเป็นแบบจำลองฐานข้อมูลที่น่าเกลียด
dr_

@AndriyM ฉันเข้าใจและฉันยอมรับว่าในกรณีส่วนใหญ่เราไม่ควรเก็บเอนทิตีที่แตกต่างกันสองประเภทไว้ในตารางเดียวกัน อย่างไรก็ตามอ่านข้อแม้ของฉัน: ถ้างานและอันตรายอยู่ในความสัมพันธ์แบบ 1 ต่อ 1 และมีอยู่สำหรับสิ่งนี้เท่านั้นก็ยอมรับได้ที่จะใช้ตารางเดียว
dr_

1

อีกวิธีที่ดูเหมือนจะไม่กล่าวถึงก็คือการมี Hazards และ Tasks ใช้พื้นที่ ID เดียวกัน หาก Hazard มีภารกิจก็จะมี ID เดียวกัน หากภารกิจมีไว้สำหรับสิ่งที่เป็นอันตรายมันจะมี ID เดียวกัน

คุณจะใช้ลำดับแทนคอลัมน์ข้อมูลประจำตัวเพื่อเติมข้อมูล ID เหล่านี้

แบบสอบถามในรูปแบบข้อมูลชนิดนี้จะใช้การรวมภายนอก (เต็ม) เพื่อดึงผลลัพธ์

วิธีนี้คล้ายกับคำตอบของ @ AndriyM มากยกเว้นคำตอบของเขาอนุญาตให้ ID ต่างออกไปและตารางเพื่อเก็บความสัมพันธ์นี้

ฉันไม่แน่ใจว่าคุณต้องการใช้วิธีนี้สำหรับสถานการณ์สองตาราง แต่ทำงานได้ดีเมื่อจำนวนตารางที่เกี่ยวข้องเพิ่มขึ้น


ขอบคุณสำหรับการสนับสนุนฉันได้พิจารณาวิธีการเช่นกัน ฉันตัดสินใจกับมันในท้ายที่สุดเพราะลำดับที่น่าอึดอัดใจในการใช้งานใน sql2008 และปัญหามากกว่าที่ฉันยินดีที่จะทน
Andrew Savinykh
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.