2011-07-14 63 views
3

什麼是最好的方式插入表中引用1到1彼此的表?MySQL數據庫設計。在1to1表中插入行。

我的意思是,在MySQL 5.5和表InnoDB的,我有類似以下 enter image description here

數據庫設計的時候,我們嘗試在Table 1和表2中插入行,就會出現問題。由於在MySQL中沒有多表插入,所以我不能插入一行,因爲這兩個表中的外鍵都是非空字段,並且應該同時插入到兩個表中。

這是解決這個問題的方法嗎?

我記得3種可能的解決方案,但我想知道是否有更多這些或哪些是最好的,爲什麼。

  1. 將外鍵字段設置爲NULLABLE,並在表中插入一行後插入另一行,之後更新第一個。

  2. 正如上面所指出的那樣,但是具有像-1這樣的特殊值。首先,在外部插入一個key = -1的表,該表等於NULL,但避免將字段設置爲NULLABLE。之後,我們將行插入另一個表中並更新插入的第一個。

  3. 創建一個關係表,雖然兩者之間是不是真的有必要,因爲它是一個1比1的比例

謝謝!

編輯 我簡單介紹一下我所需要的這種循環關係:它是從父表中的非規範化爲它的子之一。它是按照高性能的順序製作的,始終是父表中排名最高的孩子的參考。

+3

我經常觀察到的這種關係是兩個表應該在一個表中,或者只有一個表需要參考。 – Jacob

+0

在這種情況下這是不正確的。不管怎樣,謝謝! –

+3

@Emilio:在這種情況下你可能認爲這是不正確的。但也許有辦法改變這兩張桌子上的設計,所以沒有像這樣的循環路徑。你能描述爲什麼需要這兩種關係嗎?這兩張表是什麼? –

回答

7

我因爲我覺得這是一個設計缺陷,所以我會做出這個答案。

首先,如果兩個表都是真的1:1的關係,爲什麼你不只有一個表?


其次,如果它不是一個真正的1:1關係,而是一個超亞型的問題,你也不需要這個圓形的外鍵。假設table1Employeetable2Customer。當然大多數客戶不是員工(反之亦然)。但有時客戶也可能是一名員工。這是可以解決的有3個表:

Person 
------ 
id 
PRIMARY KEY: id 

Employee 
-------- 
personid 
lastname 
firstname 
... other data 
PRIMARY KEY: personid 
FOREIGN KEY: personid 
    REFERENCES Person(id) 

Customer 
-------- 
personid 
creditCardNumber 
... other data 
PRIMARY KEY: personid 
FOREIGN KEY: personid 
    REFERENCES Person(id) 

在你描述的情況下,你有兩個表Parent並具有1:N關係Child。然後,您想要以某種方式爲每個父母存儲最佳表現(基於定義的計算)孩子。

將這項工作?:

Parent 
------ 
id 
PRIMARY KEY: id 

Child 
----- 
id 
parentid 
... other data 
PRIMARY KEY: id 
FOREIGN KEY: parentid 
    REFERENCES Parent(id) 
UNIQUE KEY: (id, parentid)    --- needed for the FK below 

BestChild 
--------- 
parentid 
childid 
... other data 
PRIMARY KEY: parentid 
FOREIGN KEY: (childid, parentid) 
    REFERENCES Child(id, parentid) 

這樣,你強制想引用完整性(每BestChild是一個孩子,每一個家長只有一個BestChild),而且在沒有引用任何圓形路徑。對最好的孩子的引用存儲在附加表中,而不是在Parent表中。

您可以通過加入找到BestChild爲每位家長:

Parent 
    JOIN BestChild 
    ON Parent.id = BestChild.parentid 
    JOIN Child 
    ON BestChild.childid = Child.id 

此外,如果要存儲多個性能測試最好的兒童(針對不同類型的測試,或在不同日期測試) ,你可以添加一個test場,並改變主鍵(test, parentid)

BestChild 
--------- 
testid 
parentid 
childid 
... other data 
PRIMARY KEY: (testid, parentid) 
FOREIGN KEY: (childid, parentid) 
    REFERENCES Child(id, parentid) 
FOREIGN KEY: testid 
    REFERENCES Test(id) 
+0

感謝您的回答!爲了澄清爲什麼需要這種關係,我編輯了這個問題。 –

+2

@Emilio:也請選中此選項。沒有涉及觸發器,沒有循環引用,額外的表被標準化。 (好吧,如果您希望每次兒童屬性更改時都更新最佳高性能子項,則可能會觸發)。 –

+0

這是一個很好的答案。謝謝! –

1

我想創建一個黑洞表,把一個觸發對照顧插入

CREATE TABLE bh_table12 (
    table1col varchar(45) not null, 
    table2col varchar(45) not null 
) ENGINE = BLACKHOLE 

,並把一個觸發上採取插入的照顧

DELIMITER $$ 

CREATE TRIGGER ai_bh_table12_each AFTER INSERT ON bh_table12 FOR EACH ROW 
BEGIN 
    DECLARE mytable1id integer; 
    DECLARE mytable2id integer; 

    SET foreign_key_checks = 0; 
    INSERT INTO table1 (table1col, table2_id) VALUES (new.table1col, 0); 
    SELECT last_insert_id() INTO mytable1id; 
    INSERT INTO table2 (table2col, table1_id) VALUES (new.table2col, table1id); 
    SELECT last_insert_id() INTO mytable2id; 
    UPDATE table1 SET table2_id = mytable2id WHERE table1.id = mytable1id; 
    SET foreign_key_checks = 1; 
END $$ 

DELIMITER ; 

注意,行動在觸發器中是一個事務的一部分(當使用InnoDB或類似的時候),所以觸發器中的錯誤會回滾部分的變化。你的表結構

注意
注意,如果是1對1的表,你只需要把table2_id在表1和表2中(反之亦然)沒有table1_id。
如果您需要根據表2可以只使用查詢表1:同樣

SELECT table1.* FROM table1 
INNER JOIN table2 on (table2.id = table1.table2_id) 
WHERE table2.table2col = 'test2' 

的另一種方式圓

SELECT table2.* FROM table2 
INNER JOIN table1 on (table2.id = table1.table2_id) 
WHERE table1.table1col = 'test1' 

鏈接:
http://dev.mysql.com/doc/refman/5.1/en/blackhole-storage-engine.html
http://dev.mysql.com/doc/refman/5.1/en/triggers.html

+0

感謝您的回答!爲了澄清爲什麼需要這種關係,我編輯了這個問題。但是我不知道黑洞引擎,請你稍微解釋一下。 再次感謝! –

+2

'blackhole'引擎是一個數據庫存儲,它忘記了你放入它的所有東西,你可以放入所有東西,但沒有任何東西出現(就像Linux上的/ dev/null)。這很有用,因爲您可以將日誌文件和觸發器附加到黑洞上,以便在不需要清理的情況下執行某些操作。 – Johan

0

我覺得這是一個重要的問題,我還沒有發現任何100 %在整個網絡中滿足答案。你給出的2個答案是我發現的最好答案,但它們不是100%滿意的。

這裏的原因:

  • 爲什麼埃米利奧不能把自己最好的孩子他的父母表裏面的原因很簡單,我想,因爲我分享了同樣的問題:不是每個孩子都會被標記爲父母最好的孩子。所以他仍然需要將信息存儲在別的地方的其他孩子身上。在那種情況下,他會得到一些有關父母表中最好的孩子的信息,以及其他孩子在一個單獨的數據庫中的信息。這是一個巨大的混亂。例如,當他想改變關於兒童的數據結構時,他需要在兩個表格中改變它。每次他寫一個查詢所有的孩子,他應該查詢兩個表等,...

  • 爲什麼Emilio不能將最好的孩子外鍵設置爲可空(我假設爲Emilio,但對我來說,它會是非常嚴格的),是他需要確定父母總是有最好的孩子。在埃米利奧的情況下,這可能不是很容易想象的,但在我的情況下,我不能讓父母沒有孩子。

因此我傾向於認爲,與設置FOREIGN_KEY_CHECKS解決零是最好的,但現在的問題是:

  • 設置FOREIGN_KEY_CHECKS回1後,沒有支票數據的一致性。因此,您在此期間有犯錯的風險。你可以認爲你不會,但它仍然不是一個非常乾淨的解決方案。