6

我有一个这样的 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

既然如此,给定提供的 entity_id 为 4,如何获取所有相关记录,即 1 和 5?

同样, entity_id = 1 的查询应返回 2、3、4 和 5。

感谢您抽出宝贵时间,如果我能澄清这个问题,请告诉我。

4

8 回答 8

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,因为您只会使用它来选择a一个中的所有bRANGE SCANonix_b会比SKIP SCANon快ix_a_b

使用您的实体填充表,如下所示:

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此处允许您使用上述索引并避免为唯一性进行额外排序。

以上所有对于对称和反自反关系都是有效的。这意味着:

  • 如果ab相关,则 ba相关

  • a从不与a相关

于 2009-01-23T19:30:42.420 回答
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'

希望有帮助。

于 2009-01-23T19:25:40.340 回答
1

链接表方法似乎很好,除了您可能想要一个“关系类型”,以便您知道它们为什么相关。

例如,Raleigh 和 North Carolina 之间的关系与 Raleigh 和 Durham 之间的关系不同。此外,您可能想知道谁是关系中的“父母”,以防您使用条件下拉菜单。(即您选择一个州,您可以看到该州的城市)。

根据您要求的复杂性,您现在拥有的简单设置可能还不够。如果您只是需要表明两条记录以某种方式相关,那么链接表就足够了。

于 2009-01-23T19:35:12.833 回答
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
于 2009-01-23T19:40:21.153 回答
0
select * from entities
where entity_id in 
(
    select entity_id_b 
    from entity_entity 
    where entity_id_a = @lookup_value
)
于 2009-01-23T19:26:16.220 回答
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

或者两个过滤的查询 UNIONed 因此:

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
于 2009-01-23T19:31:43.090 回答
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 绑定到您要查询的实体

于 2009-01-23T21:08:52.147 回答
-1

我的建议是您的初始表格设计很糟糕。不要在同一张表中存储不同类型的东西。(数据库设计的第一条规则,就是不要在同一个字段中存储多条信息)。这更难查询,并且会导致严重的性能问题。另外,将数据输入到 realtionship 表中会是一个问题——当你做一个新的条目时,你怎么知道哪些实体需要被 realted?设计适当的关系表会好得多。实体表几乎总是一个坏主意。从示例中我看不出有任何理由将此类信息放在一个表中。坦率地说,我有一个大学表和一个相关的地址表。这将很容易查询并执行得更好。

于 2009-01-23T19:53:28.117 回答