0

我試圖設計一個數據庫。我有一個設計,在我看來被標準化爲Fourth Normal Form - 但我仍然認爲它已經壞了,而且我不能爲了我的生活而弄清楚如何解決它。我應該如何規範化這個數據庫設計?

背景:我們有四種類型的測試,以及每種類型的幾十種測試。我們分批運行測試,每個批次只包含一種類型的測試。所以一個測試結果屬於一個批次,也屬於一個測試。這給數據庫的計劃是這樣的:

Diagram with Result going Many-to-One to Test and Result also going Many-to-One to Batch, and both Batch and Test going Many-to-One to TypeOfTest

的問題是,這樣的設計使得結果,該結果爲A型的試驗,但結果是在一個批次的B型

我不能做的一件事是將測試表和批表結合到一張表中。每個星期都有一個新批次,而一個測試會持續數月或數年。批處理可以包含很多測試(雖然總是具有相同的類型),而且測試通常會在許多批次中進行多次。

我可以在測試和批次之間插入多對多的連接,但我無法立即看到這將如何幫助任何事情。

是否有一種乾淨的方式來重新組織這個,以便我們沒有循環連接路徑?這是必要的嗎?還是理想?

或者我應該隨身攜帶,不再擔心嗎? :-)

[編輯1]請注意,測試詳細說明了它如何運行,誰修復了發現的問題等等,這些問題在多個批次中保持不變,因此測試必須獨立存在它可能(或可能)不)在運行

[編輯2]有人指出,這將是最好有一個TestBatch表,這給了我們這樣的結構:

Now with TestBatch in the place where Result used to be, and Result going Many-to-One to TestBatch

我同意這是一個很好的的想法,但這並不能解決問題。它只是將問題從Result移動到TestBatch。我們現在可以有一個用於A型測試的TestBatch,但是TestBatch有一個用於B型的批處理。

[編輯3]感謝@ philip-kelley的出色建議,我相信我們有一個回答。首先,我們鏈接TestBatch直接回類型,即:

enter image description here

這不會立即解決問題。事實上,它使情況變得更糟 - 現在可能有一個用於測試的類型,一個不同的類型用於批處理,以及第三個類型直接從TestBatch連接。

但第二步是將外鍵從TestBatch更改爲Test,以便它包含Type以及TestID。並將外鍵更改爲批以包含類型以及BatchID。

這樣,我們可以確定TestBatch具有與Test相同的Type和Batch。

+0

等等,我錯過了一些東西。如果'每個批次只包含一種類型的測試','一個測試結果屬於一個批次',那麼'A型測試的結果'應該如何在類型B的批次中進行? –

+0

在現實世界中 - 它不能。每個結果都是針對特定的測試和特定的批次,並且該測試和該批次*必須*具有相同的類型。 在我的數據庫設計中 - 很容易得到A類測試的結果,但結果是B類的批次。這就是爲什麼我認爲我的設計被破壞的原因。 –

回答

3

@ HLGEM的回答的那些描述邏輯模型,具有一定的物理模型的細節秒。支持並強制執行您的業務規則的物理實現看起來像這樣。 (這是僞代碼,僅顯示關鍵列 - 您想爲Name,Score等屬性添加列。實際的實現細節是依賴於系統的,可能會有點棘手,但任何RDBMS都應該能夠請注意列出的所有列都是不可空的。)

CREATE TABLE TestType 
    TestType int 
    <primary key on TestType> 


CREATE TABLE Test 
    TestId  int 
    TestType  int 
    <primary key on TestId> 
    <foreign key into TestType on column TestType> 


CREATE TABLE Batch 
    BatchId  int 
    TestType  int 
    <primary key on BatchId> 
    <foreign key into TestType on column TestType> 


CREATE TABLE TestInBatch 
    TestInBatchId int 
    TestId   int 
    BatchId  int 
    TestType  int 
    <primary key on TestInBatchId> 
    <unique constraint on TestId, BatchId> 
    <foreign key on (TestId, TestType) into Test, columns (TestId, TestType)> 
    <foreign key on (BatchId, TestType) into Batch, columns (BatchId, TestType)> 


CREATE TABLE Result 
    ResultId  int 
    TestInBatchId int 
    <primary key on ResultId> 
    <foreign key into TestInBatch on column TestInBatchId> 
+0

當然!您已在TestInBatch中添加了一個TestType字段,並將其用作Test和Batch的外鍵中的第二個字段。這意味着TestType對於給定的TestInBatch總是固定的,並且它總是匹配Test和Batch。完善! :-) –

+0

如果我們要在TestInBatch表中有一個TestType字段,要在外鍵中使用Test和Batch,那麼我可能會使用該字段作爲直接外鍵鏈接返回到TestType表好。我正在使用Laravel,所以能夠直接連接將會很方便。 –

+0

使用TestType與Test和Batch之間的外鍵,以及從它們到TestInBatch的外鍵,TestInBatch和TestType之間的外鍵不是絕對必要的......但是當它有助於實現或性能時,請繼續。 –

1

創建一個包含與特定批次關聯的測試的TestBatch表。在結果表中使用該表的PK作爲FK。

在任何情況下都需要TestBatch,因爲與特定批次關聯的測試是您需要捕獲的歷史時刻。每次構建新批次時,都可能會添加新的測試,但您不希望它們與較早完成的批次相關聯。

TestBatch加入測試和批處理,幷包含BatchID,TestID和它自己的ID。然後結果表包含來自TestBatch的ID作爲其外鍵。

所以看到針對這個結果,你會從試驗和批量表像這樣既加入結果TestBatch再抓描述細節:

Select r.ResultId, R.Col1, r.col2, b.BatchId, b.batchdate, t.testId, t.Test_description 
From Results r 
join TestBatch tb on r.TestBatchid = tb.TestBatchid 
join Batch b on tb.batchid = b.batchid 
join Test t on tb.testid = t.testid 

類型將可能被主要用於創建記錄創建批次時的TestBatch。 並加入到上述按類型過濾在這種情況下,您通常只想加入Type到Batch或Test,但不能同時加入。

向你展示一下如何使用數據(現在忘記結果表以及官方FK和PK,你可以在@PhillipKelleys中看到很好的答案)代碼是爲SQL服務器編寫的,我使用了臨時表你可以在提交一個結構之前玩一下,但是如果你想創建真正的表格,可以刪除#號。身份是什麼SQL Server使用創建自動生成的的代碼字段替代數據庫後端做同樣的事情:

Create table #type (Typeid int identity, TypeDescription varchar(100)) 

Insert into #type (TypeDescription) 
values ('Geography'), ('History'), ('Biology'), ('Math') 

Create table #Batch (BatchID int identity, TypeID int, BatchDate datetime) 

insert into #Batch (TypeID, BatchDate) 
values (1, getdate()-1), (1, getdate() +2) , (4, getdate()) 

Create table #Test (testId int identity, TestDescription varchar(50), TypeId int) 
Insert into #Test (TestDescription, TypeId) 
values ('fall midterm', 1), ('fall final', 1), ('fall midterm', 3), ('fall final', 3), ('fall final', 2), ('fall midterm', 4), ('fall final', 4) 

Create table #TESTBATCH (TestBatchID int identity, TestID int, BATCHID int) 

Insert into #testBatch (BATCHID, TestID) 
values(1, 1), (1, 2), (2,1), (2,2), (3,6), (3, 7) 

select * from #type 
select * from #Batch 
select * from #test 
select * from #testBatch 

這將顯示所有當前批次

select B.batchdate, t.TypeDescription, te.TestDescription, t2.TypeDescription 
from #testBatch tb 
join #batch b on b.batchid = tb.batchid 
join #type t on t.typeid = b.typeid 
join #test te on te.testid = tb.testid 
join #type t2 on t2.typeid = te.typeid 

這個細節將顯示當前所有的測試,即使沒有當前批

select te.TestDescription, t2.TypeDescription, B.batchdate, t.TypeDescription 
from #test te 
join #type t2 on t2.typeid = te.typeid 
left join #testBAtch tb on te.testID = tb.testId 
left join #batch b on b.batchid = tb.batchid 
left join #type t on t.typeid = b.typeid 
+0

好點 - 我可以看到一個TestBatch表是值得擁有的。 但是它不能解決原來的問題。這只是說我在TestBatch而不是Result中遇到了問題。 例如 - 你建議我一般只想加入Type到Batch或Test,但不能同時加入。我的問題是 - 如果我從TestBatch加入批處理到輸入一個函數,但是我從TestBatch加入測試到另一個輸入類型,該怎麼辦?如果批次的測試類型不同,該怎麼辦? 對於同一個TestBatch,我會有兩種不同的類型,這取決於我所處的功能。 –

+0

類型是查找表。當你想查找指定類型的文本值時,你只想在連接中使用它。如果您在同一查詢中同時需要測試和批處理,則可以加入兩次,但通過測試批次維護真實關係。一般來說,既然你說一個批次只是針對一種類型,你只能通過批量加入Type,除非batch根本不是查詢的一部分。 – HLGEM