在SQL中建模国家邻接

时间:2009-11-11 20:09:07

标签: sql mysql graph

我正在尝试模拟MySQL中哪些国家/地区相互接壤。我有三张桌子:

nodes
-----
node_id MEDIUMINT

countries
---------
country_id MEDIUMINT (used as a foreign key for nodes.node_id)
country CHAR(64)
iso_code CHAR(2)

node_adjacency
--------------
node_id_1 MEDIUMINT (used as a foreign key for nodes.node_id)
node_id_2 MEDIUMINT (used as a foreign key for nodes.node_id)

我很欣赏节点表在这个例子中是多余的,但这是一个更大的架构的一部分,其中节点可以代表除国家之外的许多其他项目。

以下是一些数据(ID(显示在所有三个表格中)和国家/地区)

59  Bosnia and Herzegovina
86  Croatia
130 Hungary
178 Montenegro
227 Serbia
232 Slovenia

克罗地亚与所有其他国家接壤,这在node_adjacency表中表示为:

59  86
86  130
86  178
86  227
86  232

因此塞尔维亚的ID可能显示为node_id_1node_id_2。该表中的数据基本上是非有向图数据。

问题:

鉴于名称'Croatia',我应该用什么SQL来检索它的邻居?

Bosnia and Herzegovina
Hungary
Montenegro
Serbia
Slovenia

将邻接信息存储为有向图数据会有任何检索效率提升吗?例如。克罗地亚与匈牙利接壤,匈牙利与克罗地亚接壤,基本上重复了对这种关系的存储:

86  130
130 86

5 个答案:

答案 0 :(得分:3)

这只是我的头脑,所以我不知道它是否是最高效的解决方案,它可能需要调整,但我认为它应该有效:

SELECT
     BORDER.country
FROM
     Countries AS C
LEFT OUTER JOIN Node_Adjacency NA1 ON
     NA1.node_id_1 = C.country_id OR
     NA1.node_id_2 = C.country_id
INNER JOIN Countries AS BORDER ON
     (
     BORDER.country_id = NA1.node_id_1 OR
     BORDER.country_id = NA1.node_id_2
     ) AND
     BORDER.country_id <> C.country_id
 WHERE
     C.country = 'CROATIA'        

由于您的图表不是定向的,我认为将其存储为有向图是不合理的。您可能还想要谷歌“Celko SQL Graph”,因为他已经在SQL中对树,图和层次结构做了大量的高级工作,并且有一本很好的专门讨论这个主题的书。

答案 1 :(得分:2)

我会存储两种关系(匈牙利与克罗地亚接壤,克罗地亚与匈牙利接壤),因此您只需要查询一列。

SELECT c.country FROM countries AS c 
INNER JOIN node_adjacency AS n 
ON n.node_id_1 = c.countryID
WHERE c.countryID = 86

答案 2 :(得分:2)

要做两个列,只需将两个查询合并在一起(借用Matthew Jones):

SELECT c.country FROM countries AS c 
INNER JOIN node_adjacency AS n 
ON n.node_id_1 = c.countryID
WHERE c.countryID = 86
UNION
SELECT c.country FROM countries AS c 
INNER JOIN node_adjacency AS n 
ON n.node_id_2 = c.countryID
WHERE c.countryID = 86

如果你这样做,你复制查询而不是数据(使用50%的空间),它仍然非常简单。

答案 3 :(得分:1)

如果您正在重复关系(即国家A与B边界,B与A边界),您可以通过简单的选择获得一种方式。如果每对国家只存储一个关系,则需要在node_adjacency表中的两列中搜索(运行两个select语句并执行联合)。

答案 4 :(得分:1)

您可以创建联合视图以避免重复:

CREATE VIEW adjacency_view (node_id_1, node_id_2)
AS
SELECT node_id_1, node_id_2 FROM node_adjacency
UNION ALL
SELECT node_id_2, node_id_1 FROM node_adjacency

所以你的查询变得非常简单:

SELECT c1.country
FROM adjacency_view
INNER JOIN countries AS c1 on c1.country_id = adjacency_view.node_id_1
INNER JOIN countries AS c2 on c2.country_id = adjacency_view.node_id_2
WHERE c2.country = 'CROATIA'