什么是最好的方式来表示很多对多关系之间记录在一个单一的SQL表?
-
19-08-2019 - |
题
我有一个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_id4,将如何一项获得所有相关的记录,这将是1和5个?
同样的查询entity_id=1应该返回2,3,4和5。
感谢您的时间,让我知道如果我可以澄清的问题。
解决方案
定义的约束: 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
's在一个 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
这里可以让你使用上述指数,并避免额外的排序的独特性。
所有上面是有效的对称和抗反思的关系。这意味着:
如果 一个 是关系到 b, 然后 b 是关系到 一个
一个 是不相关 一个
其他提示
我想你建议的结构是好的。
要获得相关的记录做一些像
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'
希望有所帮助。
链接表方法似乎不错,但您可能需要一个“关系型”,让你知道他们为什么是相关的。
例如,罗利和北卡罗来纳之间的关系是不一样的罗利和达勒姆之间的关系。此外,你可能想知道是谁在关系的“父”,如果你在开车条件下拉菜单。 (即您可以选择一个国家,你能看到的是在该州的城市)。
根据您的要求的复杂性,简单的设置,你现在所拥有的可能是不够的。如果你只需要显示两条记录以某种方式相关,链接表应该是足够了。
我已经发布了一个办法做到这一点在你的设计,但我也想,如果你在你的设计有一定的灵活性,这更加紧密地适合您的需要提供这种独立的设计洞察力。
如果该项目是在(非重叠)等价类,你可能想使等价类表的设计,其中在课堂上的一切被认为是等效的基础。类本身可以是匿名的:
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
我能想到的一些方法。
一个单次通过与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
select * from entities
where entity_id in
(
select entity_id_b
from entity_entity
where entity_id_a = @lookup_value
)
根据您的更新模式这个查询应该工作:
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势必要查询的实体
我的建议是,你INTIAL表的设计是坏的。不要存放不同类型的东西在同一个表。 (数据库设计的第一条规则,权利在那里与不存储在同一领域的多条信息)。这是更难查询并会导致显著的性能问题的道路。加上它会是输入数据到realtionship表有关的问题 - 你怎么知道的实体将需要realted什么,当你做一个新的项目?这将是更好的设计正确关系表。实体表几乎总是一个坏主意。我看没有理由从例子有这种类型的信息在一个表中。坦率地说我有一所大学表和相关的地址表。这将便于查询和表现得更好。