2009-01-23 70 views
6

我有一个像这样的SQL表:表示单个SQL表中的记录之间的多对多关系的最佳方式是什么?

更新:我改变了示例表,因为原始数据(州,城市,学校)的现有等级性质掩盖了事实之间需要简单的关系这几项。

entities 
id  name    
1  Apple  
2  Orange    
3  Banana    
4  Carrot     
5  Mushroom   

我想定义这些实体之间的双向关系,因此查看一个实体的用户可以看到所有相关实体的列表。

关系由最终用户定义。

在数据库中表示这些关系并随后查询和更新它们的最佳方式是什么?我看到它

一种方式...

我的直觉说,一个关系表如下所示:

entity_entity 
entity_id_a  entity_id_b 
1     2 
5     1 
4     1 
5     4 
1     3 

既然如此,鉴于4提供ENTITY_ID,怎么会一个获取所有相关记录,这将是1和5?

同样ENTITY_ID = 1的查询应该返回2,3,4,5

感谢您的时间,让我知道,如果我可以在任何澄清的问题。

回答

11

定义一个约束:entity_id_a < entity_id_b

创建索引:

CREATE UNIQUE INDEX ix_a_b ON entity_entity(entity_id_a, entity_id_b); 
CREATE INDEX ix_b ON entity_entity(entity_id_b); 

第二个索引并不需要包括entity_id_a因为你只会用它来一个b内选择所有a的。在ix_b上的RANGE SCAN将比在ix_a_b上的SKIP SCAN快。

填充表与实体如下:

INSERT 
INTO entity_entity (entity_id_a, entity_id_b) 
VALUES (LEAST(@id1, @id2), GREATEST(@id1, @id2)) 

然后选择:

SELECT entity_id_b 
FROM entity_entity 
WHERE entity_id_a = @id 
UNION ALL 
SELECT entity_id_a 
FROM entity_entity 
WHERE entity_id_b = @id 

UNION ALL在这里,您可以使用上述指标,避免了额外的独特性排序。

以上所有内容均适用于对称和反自反关系。这意味着:

  • 如果一个有关b,然后 b有关一个

  • 一个从未涉及一个

+0

这种方法在实践中运行良好。非常感谢你。 – GloryFish 2009-02-02 16:34:36

1

我认为你建议的结构很好。

要获得相关的记录做一些像

SELECT related.* FROM entities AS search 
LEFT JOIN entity_entity map ON map.entity_id_a = search.id 
LEFT JOIN entities AS related ON map.entity_id_b = related.id 
WHERE search.name = 'Search term' 

希望有所帮助。

+0

如果我的搜索词,其ID只发生在entity_id_b在实体匹配地图? – 2009-01-23 20:49:10

+0

换句话说,只有每个关系存储两次,相反,您的查询才有效。例如。 (1,4)和(4,1)。 – 2009-01-23 20:50:49

0
select * from entities 
where entity_id in 
(
    select entity_id_b 
    from entity_entity 
    where entity_id_a = @lookup_value 
) 
0

我能想到几个方法。

单次使用CASE:

SELECT DISTINCT 
    CASE 
     WHEN entity_id_a <> @entity_id THEN entity_id_a 
     WHEN entity_id_b <> @entity_id THEN entity_id_b 
    END AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_a = @entity_id OR entity_id_b = @entity_id 

或两个过滤查询被联合这样的:

SELECT entity_id_b AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_a = @entity_id 
UNION 
SELECT entity_id_a AS equivalent_entity 
FROM entity_entity 
WHERE entity_id_b = @entity_id 
1

链接表方法似乎不错,但您可能需要一个“关系型”,因此你知道他们为什么相关。

例如,罗利和北卡罗来纳州之间的关系与罗利和达勒姆之间的关系并不相同。此外,如果您驾驶有条件的下拉列表,您可能想知道关系中的“父母”是谁。 (即你选择一个国家,你会看到在该州的城市)。

根据您的要求的复杂性,您现在的简单设置可能不够。如果您只需要显示两条记录以某种方式相关,则链接表应该足够。

+0

我明白你的意思了。在这种情况下,我们特别不表示层次结构。在这个系统中只会有一个状态,这些关系不会被用于下钻式导航。 – GloryFish 2009-01-23 19:42:11

1

我已经在您的设计中发布了一种方法,但如果您在设计中有一定的灵活性,并且这更贴近您的需求,我也希望提供这种独立的设计见解。

如果项目在(非重叠)等价类中,您可能希望将等价类作为表设计的基础,其中类中的所有内容都被认为是等价的。类本身也可以是匿名的:

CREATE TABLE equivalence_class (
    class_id int -- surrogate, IDENTITY, autonumber, etc. 
    ,entity_id int 
) 

entity_id应该是你的空间的非重叠的分区唯一的。

这避免了确保正确的左手或右手或强制右上关系矩阵的问题。

然后将查询有一点不同:

SELECT c2.entity_id 
FROM equivalence_class c1 
INNER JOIN equivalence_class c2 
    ON c1.entity_id = @entity_id 
    AND c1.class_id = c2.class_id 
    AND c2.entity_id <> @entity_id 

或等价:

SELECT c2.entity_id 
FROM equivalence_class c1 
INNER JOIN equivalence_class c2 
    ON c1.entity_id = @entity_id 
    AND c1.class_id = c2.class_id 
    AND c2.entity_id <> c1.entity_id 
-1

我的建议是,你INTIAL表的设计是坏的。不要在同一个表中存储不同类型的东西。 (数据库设计的第一条规则,就是不要在同一个字段中存储多条信息)。这很难进行查询,并会导致重大性能问题。另外,将数据输入到关系表中会出现问题 - 您如何知道当您执行新条目时需要实体化哪些实体?正确设计关系表格会好得多。实体表几乎总是一个坏主意。在这个例子中我根本没有理由在一张表中包含这种类型的信息。坦率地说,我有一张大学表和一张相关的地址表。这很容易查询和执行得更好。

0

基于更新后的架构此查询应该工作:

select if(entity_id_a=:entity_id,entity_id_b,entity_id_a) as related_entity_id where :entity_id in (entity_id_a, entity_id_b) 

其中:ENTITY_ID势必要查询的实体

相关问题