2013-07-26 39 views
0

我想弄清楚什么是最佳数据库和表结构来存储类型(var)char的节点之间的关系。我多年前最后一次使用MySQL作为一些简单的PHP网页的后端,并且从未超越过。我希望一些经验丰富的用户可以给我他们的意见。节点关系的MySQL表

比方说,我有一大堆的名字:

  • 托马斯
  • 菲利克斯
  • 马克
  • 安妮

我现在想存储他们的关系。我的想法是有可能看起来像这样两个表:

names (id, name)  relationships (id_1, id_2) 
0 Thomas    0 1 
1 Jane     0 3 
2 Felix     1 2 
3 Marc     3 4 
4 Anne     ... 
...      

数据的范围如下:

  • 表“名称”将包含约。 500万行。
  • 表'关系'将包含150-200万行。
  • 数据库将只能由我本地访问(服务器和客户端是同一台机器)
  • 我不需要Web服务器的响应能力,在我访问它的几个场合中只有很高的吞吐量(以减少等待时间)

我的问题是:

  • 我记得正确使用PRIMARY_KEY是很重要的。我隐约记得有可能将键分配到两列(即id_1,id_2在我的情况);这有助于查询我想象?
  • 有没有办法在MySQL内部防止在插入过程中创建重复关系(例如0:4 & 4:0)?
  • MySQL默认为InnoDB。这是你会为我的场景推荐的数据库吗?

任何指针欢迎。谢谢。

+1

你还没有谈到节点关系。他们是随机分配多对多关系还是某种树形结构?这种关系是否有方向性(即亲子关系)?关于存储引擎选择,你可以谈谈你正在使用的数据访问模式和MySQL版本吗? –

+0

@Mike Brant我应该更具体,但没有考虑到这些区别。在我的情况下,这种关系不是有方向性的,即我只需要存储两个节点是否连接。没有任何类型的层次结构。每个表格将在一个长期会话中填充(首先是左侧,然后是右侧)(通过Python进行SQL查询),然后保持这种状态。稍后我会完整地读出两张表格,结合字符串及其关系。我可能会这样做十几次,直到我的最终产品令人满意。 – Adrian

回答

0

首先,你需要考虑你的关系是否有与他们相关的“方向”。例如,“是......的孩子”的关系与其他方面相同的关系有相反的方向,“是父母的”。另一方面,“是兄弟姐妹”的关系是无向的(或者是双向的,取决于个人的观点)。

您描述的结构对于定向关系是完美的。

另一方面,双向关系通常最好由故意地表示执行第二项bulletpoint中描述的复制;而这会消耗更多的存储空间,它大大简化了查询,如“寻找X的所有兄弟姐妹” —否则可能不得不采取两个单独的查询的工会:

SELECT id_2 FROM my_table WHERE id_1=X 
UNION 
SELECT id_1 FROM my_table WHERE id_2=X 

因为没有指数的结果列上,如果想要对结果做更多​​的事情(比如按id排序,或者加入names表—,尽管在特定情况下可以在联合之前执行联接,但这只是增加了这些查询可能会很慢数据操作代码中的冗余和复杂性)。

可以使用triggers确保每当写入(插入,更新或删除)关系到表示双向关系的表时,都会自动对相反关系执行相同的操作。其次,你描述的表示被称为“邻接表”,它非常简单易懂。但是,在处理通过数据层次结构的深层搜索方面并不是很好,特别是在MySQL上(与其他RDBMS不同,它不支持递归函数)。因此,找到“X的所有后代”或“Y的所有祖先”实际上是相当困难的。其他数据模型,如“nested sets”或“transitive closure”对于这些任务来说更好。

随着该序言说,对您的问题:

  • 我记得正确使用PRIMARY_KEY是很重要的。我隐约记得有可能将键分配到两列(即id_1,id_2在我的情况);这有助于查询我想象?

    有您relationship表四种可能的主键:

    • (id_1)

    • (id_2)

    • (id_1, id_2)

    • (id_2, id_1)

    根据定义,主键必须是你的表内唯一。事实上,它是主要识别记录的手段。但是,如果需要的话,还可以定义更多的UNIQUE键,它们与主键具有相同的约束效果(差异相对较小并且超出了本答案的范围):因此,实际上可以实施上述约束的任意组合。

    上述约束条件分别是:将每个名称限制在关系的一侧不超过一次;将每个姓名限制在关系的另一方不超过一次;并且最后两个限制每个组合的名字在之间相同的关系不超过一次(差别仅仅是存储索引的顺序)。如果表格表示无向关系,那么显然第二个和第四个约束在语义上分别等同于第一个和第三个约束。

    一些例子:

    • ,如果你的表是“id_1是​​遗传父亲”,那么id_1可能有许多儿童。所以(id_1)不可能是是主键,因为它不会唯一标识拥有多个孩子的父亲的记录。另一方面,​​只能有一个遗传父亲(抛开胚胎学的进展),所以(id_2)唯一地标识一条记录和可以是是主键(也就是说,这种多对一的关系可能是以及通过names表中的father_id列来模拟)。另外两个(复合)键允许儿童有许多父亲,因此必须是不正确的。

    • ,如果你的表是“id_1是​​父”,那么这两个家长可以有很多孩子孩子能有一个以上的父(这被称为一个多一对多的关系)。因此,前两个约束是不正确的,必须在后两个之间进行选择(如前所述,差异仅仅是索引存储的顺序,因此MySQL在查找第二列之前必须找到第一列)。顺便提一句,在这种情况下,人们可能会考虑在relationship表中添加一个额外的列,指出该关系表示哪个父代;如果一个孩子只能有一个父母,那么可以将主键定义为(child_id, parent_type)

    • ,如果你的表是“id_1和​​结婚”,那么这两个(id_1)(id_2)是“候选键”,因为没有人可以娶一个以上的其他人(至少在英国,一夫多妻制除外)。因此,人们可以定义(id_1),因为主键定义第二UNIQUE键,而不是(id_2)。如前所述,人们可能希望将这些记录放在—周围的表格中,并且这些约束不会妨碍这一点。

  • 是否有从内部MySQL的一种方式,以防止重复关系的建立(如0:4 & 4:0)插入时?

    是的,人们可以使用触发器:但是请注意上面关于双向关系(常常需要这种“重复”)的说法。将实施这种约束触发的一个例子是:

    CREATE TRIGGER rel_ins BEFORE INSERT ON relationships FOR EACH ROW 
    IF EXISTS (
        SELECT * FROM relationships WHERE id_1=NEW.id_2 AND id_2=NEW.id_1 
    ) THEN 
        SIGNAL SQLSTATE '45000' 
         SET MESSAGE_TEXT = 'Reverse relationship already exists'; 
    END IF;; 
    

    一个可能还需要“更新前”类似的触发器。

    这种情况下,这种约束可能是可取的将是表中“父母”的地方,因为父母不能是他们孩子的孩子(但在这种情况下,值得注意的是,在这样的情况下一个关系表,实际上可能希望进一步去阻止全部循环—例如防止孩子成为其祖父母的父母)。另一方面,“邻接表”并不是执行这种约束条件的最佳模型,另一方面,纯粹依靠其结构完全防止所有的循环。

  • MySQL默认为InnoDB。这是你会为我的场景推荐的数据库吗?

    InnoDB的最大优势在于它完全兼容ACID,从而提供事务支持。如果您可能一次向多个位置写入数据库,这将特别有用。如果您只是将一堆静态数据一次性加载到数据库中以供后续查询,它可能会比MyISAM慢一点。

+0

非常感谢您的详细解答,它帮助我取得了进步! – Adrian