簡體   English   中英

MySQL 數據庫設計。 在 1to1 表中插入行。

[英]MySQL Database design. Inserting rows in 1to1 tables.

將行插入到相互引用 1 到 1 的表中的最佳方法是什么?

我的意思是,在 MySQL 5.5 和 InnoDB 表中,我有一個類似於以下的數據庫設計在此處輸入圖像描述

當我們嘗試在 table1 和 table2 中插入行時,就會出現問題。 由於 MySQL 中沒有多表插入,因此我無法插入行,因為外鍵不是兩個表中的 NULL 字段,應該同時插入兩個表中。

解決這個問題的最佳方法是什么?

我想到了 3 種可能的解決方案,但我想知道是否還有更多,或者哪個是最好的以及為什么。

  1. 將外鍵字段設置為NULLABLE,在表中插入一行后,插入另一行,然后更新第一個。

  2. 如上所示,但具有特殊值,例如 -1。 首先,插入一個外key = -1的表,相當於 NULL 但避免將該字段設置為 NULLABLE。 之后,我們在另一個表中插入行並更新插入的第一個。

  3. 在兩者之間創建一個關系表,盡管它並不是真正必要的,因為它是 1 比 1 的比率

謝謝!!

編輯我簡要解釋了我需要這種循環關系:它是從父表到其子表的非規范化。 總是從父表中獲得排名最高的孩子的參考是為了高性能的順序。

我會回答這個問題,因為我覺得這是一個設計缺陷。

首先,如果兩張桌子是真正的1:1關系,為什么不只有一張桌子?


其次,如果這不是真正的1:1關系而是超類型-子類型問題,那么您也不需要這種循環外鍵。 假設table1Employee並且table2Customer 當然,大多數客戶不是員工(反之亦然)。 但有時客戶也可能是員工。 這可以通過 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)

在您描述的場景中,您有兩個表ParentChild具有1:N關系。 然后,您想以某種方式為每個父母存儲表現最佳(基於定義的計算)的孩子。

這會工作嗎?:

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 都是一個 Child,每個 Parent 都只有一個 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)

我會創建一個黑洞表並在其上放置一個觸發器來處理插入

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-on-1 表,則只需在 table1 中放入 table2_id,在 table2 中不要放入 table1_id(反之亦然)。
如果您需要根據 table2 查詢 table1,您可以使用:

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

我覺得這是一個重要的問題,在整個 web 中我還沒有找到任何 100% 令人滿意的答案。 您給出的 2 個答案是我找到的最好的答案,但它們並非 100% 令人滿意。

原因如下:

  • 我想 Emilio 不能將他最好的孩子放在他的 parent 表中的原因很簡單,因為我有同樣的問題:不是每個孩子都會被標記為父母最好的孩子。 所以他仍然需要將其他孩子的信息存儲在其他地方。 在這種情況下,他會在他們的父母的表中獲得一些關於最好孩子的信息,而其他孩子則在一個單獨的數據庫中。 這是一個巨大的混亂。 例如,他想更改關於children的數據結構的那一天,他需要在兩個表中都進行更改。 每次他對所有孩子編寫查詢時,他都應該查詢兩個表,等等......

  • Emilio 不能只將最佳子外鍵設置為可為空的原因(我認為 Emilio,但對我來說會非常嚴格),因為他需要確保父母總是有一個最好的孩子。 在 Emilio 的情況下,這可能不太容易想象,但在我的情況下,我不能擁有相當於父母沒有孩子的情況。

因此,我傾向於認為將 foreign_key_checks 設置為零的解決方案是最好的,但問題是:

  • 將 foreign_key_checks 設置回 1 后,不再檢查數據的一致性。 因此,您有在此期間犯錯誤的風險。 你可以認為你不會,但這仍然不是一個非常干凈的解決方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM