MySQL数据库设计。在1to1表中插入行。

时间:2011-07-14 09:54:48

标签: mysql database database-design insert relational-database

将行插入到彼此引用为1对1的表中的最佳方法是什么?

我的意思是,在MySQL 5.5和表InnoDB中,我有一个类似于以下的数据库设计 enter image description here

当我们尝试在table1和table2中插入行时出现问题。由于MySQL中没有多表插入,因此无法插入一行,因为两个表中的外键都是NOT NULL字段,并且应该同时插入两个表中。

解决这个问题的方法是什么?

我想到了3个可能的解决方案,但我想知道是否有更多这些或哪个是最好的以及为什么。

  1. 将外键字段设置为NULLABLE,在表格中插入一行后插入另一行,然后更新de first。

  2. 正如上面所说,但是有一个特殊值,如-1。首先,在一个表中插入一个等于NULL的外来key = -1,但避免将该字段设置为NULLABLE。然后,我们在另一个表中插入行并更新插入的第一行。

  3. 在两者之间创建关系表虽然没有必要,因为它是1比1的比率

  4. 谢谢!

    修改 我简要解释一下我需要这个循环关系:它是从父表到其子节点之间的非规范化。它是按照高性能顺序制作的,始终引用父表中排名最高的子项。

3 个答案:

答案 0 :(得分: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)

在您描述的方案中,您有两个表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)并且References中没有循环路径。对最佳子项的引用存储在额外表中,而不是Parent表中。

您可以通过加入以下每个家长找到BestChild:

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

此外,如果要为多个性能测试(针对不同类型的测试或不同日期的测试)存储最佳子项,则可以添加test字段,并将主键更改为{{1 }}:

(test, parentid)

答案 1 :(得分: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或类似事件时),因此触发器中的错误将回滚部分更改。

关于您的桌面结构的说明
请注意,如果它是一对一的表,您只需要在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

答案 2 :(得分:0)

我觉得这是一个重要的问题,我没有在整个网络上找到任何100%令人满意的答案。你给出的2个答案是我找到的最好的答案,但它们并不是100%令人满意。

原因如下:

  • 我认为Emilio不能把他最好的孩子放在他父母的桌子里的原因很简单,因为我有同样的问题:不是每个孩子都会被贴上父母最好的孩子的标签。所以他仍然需要将信息存储在其他地方的其他孩子身上。在这种情况下,他会得到一些关于父母表中最好的孩子以及其他孩子的信息。这是一个巨大的混乱。例如,在他想要更改有关子项的数据结构的那一天,他需要在两个表中更改它。每次他对所有孩子写一个查询,他都应该查询两个表等...

  • 为什么Emilio不能将最好的子外键设置为可以为空(我认为对于Emilio,但对我来说这将是非常严格的),是他需要确保父母总是拥有最好的儿童。在Emilio的情况下,它可能不是很容易想象,但在我的情况下,我不能拥有相当于父母没有孩子的东西。

因此,我倾向于认为将foreign_key_checks设置为零的解决方案是最好的,但问题出在这里:

  • 将foreign_key_checks设置回1后,没有检查数据的一致性。因此,在此期间您有犯错的风险。你可以认为你不会,但它仍然不是一个非常干净的解决方案。