sıfır veya bir ile sıfır veya bir


9

Sql Server'da sıfır veya bir ile sıfır veya bir ilişkisini en doğal şekilde nasıl modelleyebilirim?

Bir sitedeki tehlikeleri listeleyen bir 'Tehlike' tablosu vardır. Bir sitede yapılması gereken işler için bir 'Görev' tablosu vardır. Bazı Görevler bir tehlikeyi düzeltmek içindir, hiçbir görev birden fazla tehlikeyle başa çıkamaz. Bazı Tehlikelerin bunları düzeltmek gibi bir görevi vardır. Hiçbir tehlikenin kendisiyle ilişkili iki görevi olamaz.

Aşağıda aklıma gelen en iyisi var:

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

Bunu farklı yapar mısın? Bu kurulumdan memnun olmamamın nedeni, görevlerin ve tehlikelerin diğer görev ve tehlikeleri değil, birbirlerini işaret ettiğinden ve aynı tehlike / görevi gösteren hiçbir görev / tehlikenin olmadığından emin olmak için uygulama mantığının bulunması gerektiğidir. başka bir görev / tehlike işaret eder.

Daha iyi bir yol var mı?

resim açıklamasını buraya girin


Tehlike tablosunda TaskID'de benzersiz bir dizin ve Görev tablosunda benzersiz bir Tehlike Kimliği dizini oluşturamamanızın bir nedeni var mı? Bu, masadan sadece bir tanesine sahip olabilmeniz için bunu yapardı, yani bence ne yapmaya çalışıyorsunuz.
mskinner

@mskinner ama benzersiz değiller, birçoğu olabilir null.
Andrew Savinykh

ah, yakaladım. Bu durumda, Bay Ben-Gan, burada birden fazla null değerine izin vermek için bu kısıtlamanın nasıl oluşturulacağına dair harika bir yazı var sqlmag.com/sql-server-2008/unique-constraint-multiple-nulls . Bunun sizin için çalışacağını düşünüyorum. Değilse bana bildirin.
mskinner

Bunun bir yan notu olarak, filtrelenmiş dizinleri kullanan bir dizi sorun vardır, bu yüzden tanıdık değilseniz muhtemelen okumaya değer olacaktır. İşte bu konuda iyi bir blog. blogs.msdn.com/b/sqlprogrammability/archive/2009/06/29/… , Ancak bu özel senaryoda, diğer sorunların çok fazla kedere neden olmadığını varsayarsak, sizin için iyi çalışabilir.
mskinner

FWIW benzersiz bir filtrelenmiş endeks benzersizliği yalnızca boş olmayan satırlara uygulayabilir, örn.CREATE UNIQUE INDEX x ON dbo.Hazards(TaskID) WHERE TaskID IS NOT NULL;
Aaron Bertrand

Yanıtlar:


9

Mevcut kurulumdan yabancı anahtarlardan birini kaldırarak kendi asimetrik şema fikrinizle gidebilir veya şeyleri simetrik tutabilirsiniz, her iki yabancı anahtarı da kaldırabilir ve her referansta benzersiz bir kısıtlamayla bir bağlantı tablosu sunabilirsiniz. .

Yani, şöyle olur:

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

Ek (HazardId, TaskId)olarak, bu kombinasyonlara başka bir tablodan başvurmanız gerekirse birincil anahtar olduğunu bildirebilirsiniz. Ancak çiftleri benzersiz tutmak için birincil anahtar gereksizdir, her bir kimliğin benzersiz olması yeterlidir.


1
+1 Bunun böyle bir ilişki kurmanın en doğal yolu olduğunu düşünüyorum. ancak genellikle bir tuple yalnızca asgari ise birincil olarak bir tuple seçer Tuple tuple (HazardId, TaskId)sahiptir (HazardId)ve (TaskId)her ikisi de bu tablonun bir satırını benzersiz olarak tanımlar. Bunlardan biri birincil anahtar olarak seçilmelidir.
miracle173

2

Sonuç olarak:

  • Tehlikelerin bir veya sıfır Görevi vardır
  • Görevlerin bir veya sıfır Tehlikesi var

Görev ve Tehlike tabloları başka bir şey için kullanılıyorsa (yani görevler ve / veya tehlikeler başka verilerle ilişkilendirilmişse ve bize gösterdiğiniz model sadece ilgili alanları göstermek için basitleştirilmiştir) çözümünüzün doğru olduğunu söyleyebilirim.

Aksi takdirde, Görevler ve Tehlikeler yalnızca birbirine bağlanmışsa, iki tabloya ihtiyacınız yoktur; ilişkileri için aşağıdaki alanlarla tek bir tablo oluşturabilirsiniz:

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

1
Her durumda filtrelenmiş bir dizin olması gerektiğini açıklama özgürlüğünü aldım (sorunun açıklamasından tahmin edilebilir olsa da, tamamen açık görünmeyebilir). Bunu gereksiz hissediyorsanız veya fikri farklı bir şekilde iletmek istiyorsanız lütfen daha fazla düzenleme yapmaktan çekinmeyin.
Andriy M

Bu fikirden hala gerçekten memnun olmadığımı söylemeliyim. Sorun TaskID ve HazardID'yi manuel olarak oluşturmak zorunda kalmam. Bu 2012 olsaydı, bunun için diziler kullanmak mümkün olurdu, ancak o zaman bile bana doğru görünmüyordu. Sanırım iki şey, görevler ve tehlikeler tamamen farklı varlıklar ve bu yüzden onları tek bir masada saklama fikriyle uzlaşmak zor.
Andriy M

Bu tasarım seçilirse, neden TaskIDve 'ye ihtiyacımız var HazardID? Sen 2 BIT sütunları olabilir IsTask, IsHazardve onlardan ikisi yanlış bir sınırlama. Sonra Hazardtablo basitçe bir görünümdür: CRAETE VIEW Hazard SELECT HazardID = ID, HazardDetails, ... FROM ThisTable WHERE IsHazard = 1;ve sırasıyla Görev.
ypercubeᵀᴹ

@ypercube Şeklinde olmayan bir şeyi bir tabloda saklar ve bunun bir Görev veya Tehlike olup olmadığını söylemek için ikili bir alan kullanırsınız. Çirkin veritabanı modellemesi.
dr_

@AndriyM Anlıyorum ve çoğu durumda aynı tabloda iki tür farklı varlığı saklamamamız gerektiğine katılıyorum . Ancak, uyarılarımı okuyun: Görevler ve Tehlikeler bire bir ilişki içindeyse ve sadece bunun için mevcutsa , tek bir tablo kullanmak kabul edilebilir.
dr_

1

Henüz bahsedilmemiş gibi görünen bir diğer yaklaşım, Tehlikeler ve Görevlerin aynı kimlik alanını kullanmasıdır. Bir Tehlikenin Görevi varsa, aynı kimliğe sahip olacaktır. Bir Görev Tehlike içinse aynı kimliğe sahip olacaktır.

Bu kimlikleri doldurmak için kimlik sütunları yerine bir dizi kullanırsınız.

Bu tür veri modelindeki sorgular, sonuçlarını almak için (tam) dış birleşimleri kullanır.

Bu yaklaşım @ AndriyM'in cevabına çok benziyor, ancak yanıtı kimliklerin farklı olmasına izin veriyor ve bu ilişkiyi saklamak için bir tablo.

İki tablolu bir senaryo için bu yaklaşımı kullanmak istediğinizden emin değilim, ancak ilgili tablo sayısı arttığında iyi çalışıyor.


Katkıda bulunduğunuz için teşekkür ederim, bu yaklaşımı da düşündüm. Sonunda buna karşı karar verdim, çünkü sıra sql2008'de uygulamak garip ve katlanmak istediğimden daha fazla sorun.
Andrew Savinykh
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.