SQL实现旅行城市覆盖关系人员配对查询及优化问询
问题场景
现有一张存储人员旅行到访记录的表,包含name(姓名)、location(到访地点)两个字段,样例数据如下:
| name | location |
|---|---|
| Sandeep | Delhi |
| Sandeep | Jaipur |
| Nupur | Jammu |
| Nupur | Jaipur |
| Nupur | Delhi |
| Harsh | Jammu |
查询需求
需要输出NameA、NameB两列结果,判定规则为:NameB对应的人员,至少到访过NameA对应人员去过的所有城市,预期输出结果如下:
| NameA | NameB |
|---|---|
| Sandeep | Nupur |
| Harsh | Nupur |
当前已写出可正常运行的SQL实现,希望了解性能更好、逻辑更简洁的优化方案,原有实现代码如下:
SELECT u.source, u.target FROM (SELECT COUNT(*) AS combine, source, target FROM (SELECT a.name AS source, b.name AS target, a.location AS location FROM A a JOIN A b ON (a.location = b.location AND a.name <> b.name)) GROUP BY source, target ) u JOIN (SELECT name, COUNT(*) AS solo FROM A GROUP BY name )v ON u.source = v.name where u.combine = v.solo;
优化方案
原有实现的核心逻辑是正确的:通过自连接匹配两人共同到访的城市,对比「共同到访城市数」和「NameA的总到访城市数」,二者相等即符合要求。但存在两层冗余子查询,执行过程中会额外物化中间结果,可以做如下优化:
简化聚合写法
去掉不必要的子查询嵌套,直接通过一次自连接+分组判断完成计算,代码更简洁,执行效率更高:
SELECT a.name AS NameA, b.name AS NameB FROM A a JOIN A b ON a.location = b.location AND a.name <> b.name GROUP BY a.name, b.name HAVING COUNT(*) = (SELECT COUNT(*) FROM A WHERE name = a.name);
注意:该写法依赖「每个(name, location)组合唯一不重复」的前提,如果表中存在同一个人重复录入同一到访城市的脏数据,计数会出现偏差。
高容错集合判断写法
如果使用支持标准SQL的数据库(PostgreSQL、Oracle、MySQL 8.x等),可以用关系除法的经典NOT EXISTS写法,对脏数据容忍度更高,大数据量下可利用(name, location)联合索引获得更稳定的性能:
SELECT DISTINCT a.name AS NameA, b.name AS NameB FROM A a, A b WHERE a.name <> b.name AND NOT EXISTS ( -- 筛选出「NameA去过、但NameB没去过」的城市,不存在这类城市即符合要求 SELECT 1 FROM A a_visit WHERE a_visit.name = a.name AND NOT EXISTS ( SELECT 1 FROM A b_visit WHERE b_visit.name = b.name AND b_visit.location = a_visit.location ) );
该写法不需要做聚合计数,哪怕存在重复录入的到访记录,也能准确判断城市覆盖关系。
内容的提问来源于stack exchange,提问作者Sandeep
相关产品推荐
相关产品推荐

