如何用SQL递归CTE查找距离德国3个边境的国家?
递归CTE查找距离德国3个边境的国家
原始边境数据
| 国家1 | 国家2 | 边境长度 |
|---|---|---|
| 德国 | 法国 | 10000 |
| 西班牙 | 法国 | 10000 |
| 葡萄牙 | 西班牙 | 10000 |
| 奥地利 | 德国 | 10000 |
根据数据可知:
- 法国、奥地利距离德国1个边境
- 西班牙距离德国2个边境(经法国中转)
- 葡萄牙距离德国3个边境(经法国、西班牙中转)
需求与当前代码问题
需求是用SQL递归CTE找出所有距离德国3个边境的国家,注意国家可能出现在Country1或Country2任意一列。
你当前的写法存在核心问题:递归阶段没有关联原始borders表去探索新的接壤国家,只是对现有数据重复计数,无法找到更远的国家。
修正后的SQL代码
WITH RECURSIVE country_borders AS ( -- 锚点:获取德国的直接邻国,距离记为1 SELECT CASE WHEN Country1 = '德国' THEN Country2 ELSE Country1 END AS current_country, 1 AS borders_away, CAST('德国' AS CHAR(100)) AS visited_countries FROM borders WHERE Country1 = '德国' OR Country2 = '德国' UNION ALL -- 递归:基于当前国家,寻找未访问过的接壤国,距离+1 SELECT CASE WHEN b.Country1 = cb.current_country THEN b.Country2 ELSE b.Country1 END AS current_country, cb.borders_away + 1 AS borders_away, CONCAT(cb.visited_countries, ',', cb.current_country) AS visited_countries FROM country_borders cb JOIN borders b ON (b.Country1 = cb.current_country OR b.Country2 = cb.current_country) AND NOT FIND_IN_SET( CASE WHEN b.Country1 = cb.current_country THEN b.Country2 ELSE b.Country1 END, cb.visited_countries ) WHERE cb.borders_away < 3 ) -- 筛选出距离为3的国家,去重 SELECT DISTINCT current_country AS 距离德国3个边境的国家 FROM country_borders WHERE borders_away = 3;
关键逻辑说明
- 锚点成员:统一处理德国的直接邻国,用
current_country字段存储邻国,同时记录已访问的国家(初始为德国),避免后续循环访问。 - 递归成员:通过JOIN原始边境表,找到当前国家的所有接壤国,用
FIND_IN_SET排除已访问的国家,防止出现循环路径(如德国→法国→德国),同时将距离加1。 - 最终筛选:只保留距离为3的国家,并用
DISTINCT去除可能的重复结果。
注:不同数据库的字符串处理函数可能不同,比如PostgreSQL可替换FIND_IN_SET为NOT (string_to_array(visited_countries, ',') @> ARRAY[next_country]),SQL Server可用NOT EXISTS (SELECT 1 FROM STRING_SPLIT(visited_countries, ',') WHERE value = next_country),核心逻辑保持一致。
内容的提问来源于stack exchange,提问作者Akshith Jay
相关产品推荐
相关产品推荐

