2014-08-27 51 views
0

我有以下SQL表:防止週期和多階路徑

create table dbo.Companies (
    Id int identity not null constraint primary key clustered (Id), 
); 

create table dbo.Workers (
    Id int identity not null constraint primary key clustered (Id), 
    CompanyId int not null, 
); 

create table dbo.Evaluations (
    Id int identity not null constraint primary key clustered (Id),  
    CompanyId int not null, 
    WorkerId int not null 
) 

而以下限制:

alter table dbo.Workers 
add constraint FK_Workers_CompanyId foreign key (CompanyId) references dbo.Companies(Id) on delete cascade on update cascade; 

alter table dbo.Evaluations 
add constraint FK_Evaluations_CompanyId foreign key (CompanyId) references dbo.Companies(Id) on delete cascade on update cascade, 
    constraint FK_Evaluations_WorkerId foreign key (WorkerId) references dbo.Workers(Id) on delete no action on update no action; 

我使用實體框架,當我嘗試插入一條記錄,我得到錯誤:

Additional information: Introducing FOREIGN KEY constraint 'FK_dbo.Workers_dbo.Companies_CompanyId' on table 'Workers' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints. 

我應該改變約束還是應該改變我的數據庫設計?

讓我解釋一下爲什麼我設計我的數據庫是這樣的:

1 - 該CompanyId在工人表顯示了公司在工人現任職

2 - 公司評估中顯示工人在評估時工作的位置......而WorkerId顯然表示被評估的工人。

對我來說,這一切是有道理的,但也許我錯了......

+0

您的級聯設置有兩條路徑dbo.Evalutations從dbo.Companies。我可能會建議一種將工作人員與公司關係分離開來的設計,並將這些關係基於時間戳表,並將評估結果作爲同一時間戳表的子類,以便爲「評估」或「工作時間」評估「事件類型」列一家新公司,以及在評估過程中對工作人員在哪裏工作的看法是評估前的第一個「改變」記錄,按時間戳降序排列。 CompanyID應作爲FK在時間戳表中。 – 2014-08-27 21:16:46

+0

@JaazCole,很抱歉,我不太瞭解你對時間戳的建議。你能提供一個代碼示例嗎?這很容易理解。謝謝。 – 2014-08-27 21:19:16

回答

1

更明確:

CREATE TABLE dbo.EventTypes (
     EID INT PRIMARY KEY IDENTITY(1,1) 
    , EventDescription VARCHAR(40) 
); 
INSERT INTO dbo.EventTypes (EventDescription) VALUES ('Hired'); 
INSERT INTO dbo.EventTypes (EventDescription) VALUES ('Evaluated'); 

這些將被使用和評價,以確定發生了什麼事時,當在下面。

CREATE TABLE dbo.Companies (
     CID INT PRIMARY KEY IDENTITY(1,1) 
    /* ... */ 
); 
CREATE TABLE dbo.Workers (
     WID INT PRIMARY KEY IDENTITY(1,1) 
    /* ... */ 
); 

CREATE TABLE dbo.History (
     HistID INT PRIMARY KEY IDENTITY(1,1) 
    , TS DATETIME NOT NULL DEFAULT GETDATE() 
    , CompanyID INT NOT NULL FOREIGN KEY REFERENCES dbo.Companies(CID) ON UPDATE CASCADE ON DELETE CASCADE 
    , WorkerID INT NOT NULL FOREIGN KEY REFERENCES dbo.Workers(WID) ON UPDATE CASCADE ON DELETE CASCADE 
    , EventTypeID INT NOT NULL FOREIGN KEY REFERENCES dbo.EventTypes(EID) ON UPDATE CASCADE ON DELETE CASCADE 
    /* ...other generic history-traits... */ 
); 
CREATE NONCLUSTERED INDEX Idx_History_TS ON dbo.History (TS) INCLUDE (CompanyID, WorkerID, EventTypeID) WITH (FILLFACTOR = 90); 
CREATE NONCLUSTERED INDEX Idx_History_CompanyID ON dbo.History (CompanyID) WITH (FILLFACTOR = 90); 
CREATE NONCLUSTERED INDEX Idx_History_WorkerID ON dbo.History (WorkerID) WITH (FILLFACTOR = 90); 
CREATE NONCLUSTERED INDEX Idx_History_EventTypeID ON dbo.History (EventTypeID) WITH (FILLFACTOR = 90); 

這將包含一堆事件,並且您可以將事件分類爲您喜歡的事件。你的代碼可以,例如,看這裏當相關EventDescription =「評估」:

CREATE TABLE dbo.Evaluations (
     EvalID INT NOT NULL PRIMARY KEY FOREIGN KEY REFERENCES dbo.History(HistID) ON UPDATE CASCADE ON DELETE CASCADE 
    /* ...Eval columns... */ 
); 

CREATE TABLE dbo.EvaluationItems (
     ID INT PRIMARY KEY IDENTITY(1,1) 
    , EvalID INT NOT NULL FOREIGN KEY REFERENCES dbo.Evaluations (EvalID) 
    /* ...item details... */ 
); 
GO 

這只是一個模型提供,評價項目不必是這樣的。

其次,最近的僱主:

CREATE VIEW dbo.WorkersLastEmployer AS 
    SELECT W.*, C.* 
    FROM dbo.Workers W 
     INNER JOIN (
      SELECT H.HistID, H.TS, H.CompanyID, H.WorkerID, MAX(TS)OVER(PARTITION BY H.WorkerID) AS LastHired 
      FROM dbo.History H 
       INNER JOIN dbo.EventTypes E ON E.EID = H.EventTypeID 
      WHERE E.EventDescription = 'Hired' 
     ) Hi ON Hi.WorkerID = W.WID 
      AND Hi.TS = Hi.LastHired 
     INNER JOIN dbo.Companies C on C.CID = Hi.CompanyID 
GO 

而且,由於CompanyID被遷移到歷史和評估現在是一個直接的子類,這一觀點爲作業者X的評估在公司Y:

CREATE VIEW dbo.WorkerEvalutations AS 
    SELECT E.*, W.*, C.* 
    FROM dbo.Evaluations E 
     INNER JOIN dbo.History H on E.EvalID = H.HistID 
     INNER JOIN dbo.Workers W on W.WID = H.WorkerID 
     INNER JOIN dbo.Companies C on C.CID = H.CompanyID 
GO 

這個結構運行時沒有警告,並會給你一個更容易擴展的框架。