1. 什么是语句组合 #
在实际应用中,我们经常需要将多个查询的结果组合在一起,或者在查询过程中进行中间处理。Cypher提供了两种主要的方式来处理这些需求:
- UNION操作:将多个查询的结果合并在一起
- WITH子句:在查询过程中传递数据,进行中间处理
1.1 为什么需要语句组合? #
想象一下,如果你需要:
- 查找所有演员和导演(需要合并两种不同的查询结果)
- 先统计每个人的电影数量,然后只显示参演超过2部电影的人(需要中间处理)
- 查找每个人的朋友,然后查找这些朋友喜欢的技术(需要链式处理)
这些场景都需要使用语句组合功能。
2. UNION操作 #
UNION操作用于将两个或多个查询的结果合并在一起。它类似于SQL中的UNION操作。
2.1 前置知识:UNION操作 #
UNION操作就像把两个篮子的东西合并到一个篮子里:
- 如果两个篮子里有相同的东西(重复),UNION会自动去除重复
- UNION ALL会保留所有东西,包括重复的
2.2 UNION的基本语法 #
查询1
UNION [ALL]
查询22.3 UNION vs UNION ALL #
| 操作 | 说明 | 行为 | 使用场景 |
|---|---|---|---|
| UNION | 组合结果并去除重复 | 自动去除重复行 | 需要唯一结果时 |
| UNION ALL | 组合结果但保留重复 | 保留所有行,包括重复 | 需要保留所有结果时 |
2.4 重要提示 #
使用UNION时,必须满足以下条件:
- 所有查询的返回列数量必须相同
- 所有查询的返回列类型必须匹配
- 所有查询的返回列别名必须相同
2.5 基本UNION操作 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (p1:Person {name: "张三"}),
(p2:Person {name: "李四"}),
(p3:Person {name: "王五"}),
(m1:Movie {title: "长津湖"}),
(m2:Movie {title: "满江红"}),
(p1)-[:ACTED_IN]->(m1),
(p2)-[:DIRECTED]->(m1),
(p3)-[:ACTED_IN]->(m2)
// 查询插入后的数据
MATCH (p:Person)-[r]->(m:Movie)
RETURN p.name AS Person, type(r) AS Relationship, m.title AS Movie
ORDER BY Person
// 查找所有演员和导演(UNION会自动去除重复的Person)
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name AS name, 'Actor' AS role
UNION
MATCH (p:Person)-[:DIRECTED]->(m:Movie)
RETURN p.name AS name, 'Director' AS role
ORDER BY name
// 查询最终结果
MATCH (p:Person)
RETURN p.name AS name
ORDER BY name2.6 UNION vs UNION ALL #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (p1:Person {name: "张三", city: "北京"}),
(p2:Person {name: "李四", city: "上海"}),
(p3:Person {name: "王五", city: "北京"})
// 查询插入后的数据
MATCH (p:Person)
RETURN p.name, p.city
// 使用 UNION(自动去重)
MATCH (p:Person {city: "北京"})
RETURN p.city AS city
UNION
MATCH (p:Person {city: "北京"})
RETURN p.city AS city
// 使用 UNION ALL(保留重复)
MATCH (p:Person {city: "北京"})
RETURN p.city AS city
UNION ALL
MATCH (p:Person {city: "北京"})
RETURN p.city AS city3. WITH子句 #
WITH子句是Cypher中非常重要的功能,它允许你在查询过程中传递数据,进行中间处理。
3.1 前置知识:数据流管道 #
WITH子句就像工厂的流水线:
- 第一步处理完的数据,通过WITH传递给第二步
- 第二步可以对这些数据进行进一步处理
- 可以有多条流水线(多个WITH子句)
3.2 WITH子句的作用 #
WITH子句用于:
- 组合各个查询部分:将多个查询步骤连接起来
- 声明数据流:指定哪些数据从一个部分流向另一个部分
- 执行中间计算:在查询过程中进行计算和过滤
- 类似于RETURN:但不结束查询,而是继续传递数据
3.3 WITH vs RETURN #
| 特性 | WITH | RETURN |
|---|---|---|
| 作用 | 传递数据到下一部分 | 返回最终结果 |
| 查询继续 | 查询继续 | ❌ 查询结束 |
| 使用场景 | 中间处理、过滤 | 最终输出 |
| 可以多次使用 | 可以链式使用 | ❌ 通常只使用一次 |
3.4 过滤聚合结果 #
WITH最常见的用途是在聚合后进行过滤。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (p1:Person {name: "张三"}),
(p2:Person {name: "李四"}),
(p3:Person {name: "王五"}),
(m1:Movie {title: "长津湖"}),
(m2:Movie {title: "满江红"}),
(m3:Movie {title: "功夫"}),
(p1)-[:ACTED_IN]->(m1),
(p1)-[:ACTED_IN]->(m2),
(p2)-[:ACTED_IN]->(m1),
(p3)-[:ACTED_IN]->(m3)
// 查询插入后的数据
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, m.title
// 使用 WITH 在聚合后进行过滤
// 先统计每个人参演的电影数量
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WITH p, count(m) AS movieCount
// 然后过滤出参演超过1部电影的人
WHERE movieCount > 1
RETURN p.name, movieCount
// 查询最终结果
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WITH p, count(m) AS movieCount
WHERE movieCount > 1
RETURN p.name, movieCount
ORDER BY movieCount DESC3.5 分组和收集 #
WITH可以用于按某个字段分组,并收集相关数据。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (p1:Person {name: "张三", city: "北京"}),
(p2:Person {name: "李四", city: "北京"}),
(p3:Person {name: "王五", city: "上海"}),
(p4:Person {name: "赵六", city: "上海"}),
(m1:Movie {title: "长津湖"}),
(m2:Movie {title: "满江红"}),
(p1)-[:ACTED_IN]->(m1),
(p2)-[:ACTED_IN]->(m1),
(p3)-[:ACTED_IN]->(m2),
(p4)-[:ACTED_IN]->(m2)
// 查询插入后的数据
MATCH (p:Person)
RETURN p.name, p.city
// 使用 WITH 按城市分组,并收集每个城市的人名
MATCH (p:Person)
WITH p.city AS city, collect(p.name) AS people
RETURN city, people, size(people) AS count
// 查询最终结果
MATCH (p:Person)
WITH p.city AS city, collect(p.name) AS people
RETURN city, people
ORDER BY city3.6 链式处理 #
可以使用多个WITH子句进行链式处理,逐步处理数据。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (p1:Person {name: "张三"}),
(p2:Person {name: "李四"}),
(p3:Person {name: "王五"}),
(c1:Company {name: "科技公司"}),
(t1:Technology {name: "Python"}),
(t2:Technology {name: "Java"}),
(p1)-[:WORKS_FOR]->(c1),
(p2)-[:WORKS_FOR]->(c1),
(p1)-[:LIKES]->(t1),
(p1)-[:LIKES]->(t2),
(p2)-[:LIKES]->(t1)
// 查询插入后的数据
MATCH (p:Person)-[r]->(target)
RETURN p.name, type(r), target
// 使用多个 WITH 子句进行链式处理
// 第一步:查找有工作关系的人
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
WITH p, c
// 第二步:查找这些人喜欢的技术
MATCH (p)-[:LIKES]->(t:Technology)
WITH p, c, collect(t.name) AS technologies
// 第三步:返回结果
RETURN p.name AS person, c.name AS company, technologies
// 查询最终结果
MATCH (p:Person)-[:WORKS_FOR]->(c:Company)
WITH p, c
MATCH (p)-[:LIKES]->(t:Technology)
WITH p, c, collect(t.name) AS technologies
RETURN p.name, c.name, technologies3.7 设置查询参数 #
WITH可以用于在查询开始时设置参数,使查询更灵活和可读。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (p1:Person {name: "张三", age: 30, yearsExperience: 5}),
(p2:Person {name: "李四", age: 25, yearsExperience: 3}),
(p3:Person {name: "王五", age: 35, yearsExperience: 8}),
(p4:Person {name: "赵六", age: 28, yearsExperience: 6})
// 查询插入后的数据
MATCH (p:Person)
RETURN p.name, p.age, p.yearsExperience
// 使用 WITH 设置查询参数
WITH 3 AS minExperience, 7 AS maxExperience
MATCH (p:Person)
WHERE minExperience <= p.yearsExperience <= maxExperience
RETURN p.name, p.yearsExperience
// 查询最终结果
WITH 3 AS minExperience, 7 AS maxExperience
MATCH (p:Person)
WHERE minExperience <= p.yearsExperience <= maxExperience
RETURN p.name, p.yearsExperience
ORDER BY p.yearsExperience4. 小结 #
4.1 核心概念 #
| 概念 | 说明 | 主要用途 |
|---|---|---|
| UNION | 合并查询结果并去重 | 组合多个查询的结果 |
| UNION ALL | 合并查询结果但保留重复 | 保留所有结果,包括重复 |
| WITH | 传递数据到下一部分 | 中间处理、过滤聚合结果 |
4.2 最佳实践 #
- 使用UNION合并结果:当需要组合多个查询的结果时
- 使用WITH进行中间处理:当需要在聚合后进行过滤时
- 使用链式WITH:当需要多步处理数据时
- 使用WITH设置参数:使查询更灵活和可读
4.3 常见用法 #
- 使用UNION合并不同查询的结果
- 使用WITH在聚合后进行过滤
- 使用WITH进行分组和收集
- 使用链式WITH进行多步处理
- 使用WITH设置查询参数