我有一个像这样的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。
感谢您的时间,让我知道我是否可以澄清这个问题。
答案 0 :(得分: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
中选择所有b
。 RANGE SCAN
上的ix_b
将比SKIP SCAN
上的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
这里允许您使用上面的索引并避免对唯一性进行额外排序。
以上所有都适用于对称和反自我关系。这意味着:
如果 a 与 b 相关,那么 b 与 a
a 永远不会与 a
答案 1 :(得分: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'
希望有所帮助。
答案 2 :(得分:1)
链接表方法看起来很好,除了你可能想要一个'关系类型',这样你就知道为什么它们是相关的。
例如,罗利和北卡罗来纳州之间的关系与罗利和达勒姆之间的关系不同。此外,如果您正在推动条件下拉菜单,您可能想知道关系中的“父母”是谁。 (即你选择一个州,你可以看到该州的城市)。
根据您的要求的复杂程度,您现在的简单设置可能还不够。如果您只需要以某种方式显示两条记录是相关的,那么链接表就足够了。
答案 3 :(得分: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
答案 4 :(得分:0)
select * from entities
where entity_id in
(
select entity_id_b
from entity_entity
where entity_id_a = @lookup_value
)
答案 5 :(得分: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
答案 6 :(得分: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绑定到您要查询的实体
答案 7 :(得分:-1)
我的建议是你的初始桌面设计不好。不要在同一个表中存储不同类型的东西。 (数据库设计的第一条规则,在那里不要在同一领域存储多条信息)。这样查询起来要困难得多,并且会导致严重的性能问题。此外,将数据输入到实际关系表中会出现问题 - 如何知道在进行新条目时需要实现哪些实体?设计适当的关系表会好得多。实体表几乎总是一个坏主意。我认为这个例子中没有任何理由在一个表中提供这种类型的信息。坦率地说,我有一张大学桌子和一张相关的地址表。它很容易查询和执行得更好。