1. 什么是子查询 #
子查询是嵌套在另一个查询内部的查询。它就像在一个大问题中问一个小问题,然后用小问题的答案来帮助回答大问题。
1.1 前置知识:子查询的概念 #
想象一下,如果你要查找"所有在Neo4j公司工作的人的朋友",你需要:
- 先找到在Neo4j公司工作的人(这是子查询)
- 然后找到这些人的朋友(这是主查询)
子查询就是用来完成第一步的。
1.2 为什么需要子查询? #
子查询可以帮助我们:
- 简化复杂查询:将复杂问题分解为多个小问题
- 提高代码可读性:使查询逻辑更清晰
- 实现复杂过滤:在WHERE子句中进行复杂的条件检查
- 组合多个查询结果:将不同查询的结果合并在一起
1.3 Neo4j中的子查询类型 #
Neo4j支持以下类型的子查询(从Neo4j 4.0开始):
| 子查询类型 | 描述 | 版本 | 主要用途 |
|---|---|---|---|
| EXISTS | 检查是否存在匹配模式 | Neo4j 4.0+ | 模式存在性检查 |
| COUNT | 计算匹配模式的数量 | Neo4j 4.0+ | 需要知道匹配数量 |
| CALL {...} | 执行结果返回子查询 | Neo4j 4.0+ | 组合多个查询结果 |
1.4 关于子查询的重要事项 #
使用子查询时,需要注意以下几点:
子查询由大括号
{ }界定- 所有子查询都必须用大括号包围
变量作用域
- 子查询可以访问外部查询的变量
- 子查询中的变量不会影响外部查询(除非通过RETURN返回)
必须显式传递变量
- 如果子查询需要使用外部查询的变量,必须显式传递
返回要求
- EXISTS子查询只需要返回至少一行(返回true/false)
- CALL子查询必须返回结果
- COUNT子查询返回数字
2. EXISTS子查询 #
EXISTS子查询用于检查是否存在匹配的模式。它返回布尔值(true/false),就像问"有没有这样的数据?"
2.1 前置知识:EXISTS的作用 #
EXISTS就像问一个问题:"有没有符合条件的数据?"
- 如果有:返回true
- 如果没有:返回false
2.2 EXISTS子查询的基本语法 #
WHERE EXISTS {
MATCH ...
[WHERE ...]
RETURN ...
}2.3 基本EXISTS用法 #
最简单的EXISTS子查询用于检查某个模式是否存在。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (jennifer:Person {name: 'Jennifer'}),
(mark:Person {name: 'Mark'}),
(john:Person {name: 'John'}),
(neo4j:Company {name: 'Neo4j'}),
(xyz:Company {name: 'XYZ'}),
(jennifer)-[:IS_FRIENDS_WITH]->(mark),
(jennifer)-[:IS_FRIENDS_WITH]->(john),
(jennifer)-[:WORKS_FOR]->(neo4j),
(mark)-[:WORKS_FOR]->(xyz)
// 查询插入后的数据
MATCH (p:Person)-[r]->(target)
RETURN p.name, type(r), target
// 使用EXISTS子查询查找在Neo4j工作的人的朋友
// 第一步:MATCH匹配所有朋友关系
// 第二步:WHERE EXISTS检查这个人是否在Neo4j公司工作
// EXISTS子查询中的变量p来自外部查询
// 如果子查询返回至少一行,EXISTS返回true
MATCH (p:Person)-[r:IS_FRIENDS_WITH]->(friend:Person)
WHERE EXISTS {
MATCH (p)-[:WORKS_FOR]->(:Company {name: 'Neo4j'})
}
RETURN p.name AS person, friend.name AS friend
ORDER BY p.name, friend.name
// 查询最终结果
MATCH (p:Person)-[r:IS_FRIENDS_WITH]->(friend:Person)
WHERE EXISTS {
MATCH (p)-[:WORKS_FOR]->(:Company {name: 'Neo4j'})
}
RETURN p.name, friend.name
ORDER BY p.name, friend.name2.4 复杂EXISTS用法 #
EXISTS子查询可以包含复杂的逻辑,包括多个MATCH和WHERE条件。
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (diana:Person {name: 'Diana'}),
(melissa:Person {name: 'Melissa'}),
(graphs:Technology {type: 'Graphs'}),
(java:Technology {type: 'Java'}),
(query:Technology {type: 'Query Languages'}),
(companyX:Company {name: 'Company X'}),
(companyA:Company {name: 'Company A'}),
(diana)-[:WORKS_FOR]->(companyX),
(melissa)-[:WORKS_FOR]->(companyA),
(diana)-[:LIKES]->(graphs),
(melissa)-[:LIKES]->(graphs),
(diana)-[:LIKES]->(query),
(melissa)-[:LIKES]->(query),
(john:Person {name: 'John'})-[:LIKES]->(java),
(jennifer:Person {name: 'Jennifer'})-[:LIKES]->(graphs)
// 查询插入后的数据
MATCH (p:Person)-[r]->(target)
RETURN p.name AS Person, type(r) AS Relationship, target.name AS TargetName, target.type AS TargetType
ORDER BY Person
// 查找满足以下条件的人:
// 1. 在名称以"Company"开头的公司工作
// 2. 喜欢至少1种被3人或以上喜欢的技术
//
// 第一步:MATCH匹配在Company开头的公司工作的人
// 第二步:WHERE EXISTS检查是否存在被3人以上喜欢的技术
// 在EXISTS子查询中,使用COUNT子查询计算喜欢该技术的人数
MATCH (person:Person)-[:WORKS_FOR]->(company)
WHERE company.name STARTS WITH "Company"
AND EXISTS {
MATCH (person)-[:LIKES]->(t:Technology)
WHERE COUNT { (t)<-[:LIKES]-(:Person) } >= 3
}
RETURN person.name AS person, company.name AS company
ORDER BY person.name
// 查询最终结果
MATCH (person:Person)-[:WORKS_FOR]->(company)
WHERE company.name STARTS WITH "Company"
AND EXISTS {
MATCH (person)-[:LIKES]->(t:Technology)
WHERE COUNT { (t)<-[:LIKES]-(:Person) } >= 3
}
RETURN person.name, company.name
ORDER BY person.name3. COUNT子查询 #
COUNT子查询用于计算匹配模式的数量。它在WHERE子句中使用,就像问"有多少个符合条件的数据?"
3.1 前置知识:COUNT的作用 #
COUNT就像数数:"有多少个符合条件的数据?"
- 返回一个数字
- 可以用于比较(如 > 1, >= 3等)
3.2 COUNT子查询的基本语法 #
WHERE COUNT { 模式 } 操作符 数值3.3 COUNT vs EXISTS #
| 特性 | COUNT | EXISTS |
|---|---|---|
| 返回值 | 数字 | 布尔值(true/false) |
| 性能 | 需要计算所有匹配 | 找到第一个就返回 |
| 使用场景 | 需要知道具体数量 | 只需要知道是否存在 |
| 效率 | 较慢 | 较快 |
3.4 基本COUNT用法 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (jennifer:Person {name: 'Jennifer'}),
(mark:Person {name: 'Mark'}),
(john:Person {name: 'John'}),
(sally:Person {name: 'Sally'}),
(ann:Person {name: 'Ann'}),
(jennifer)-[:IS_FRIENDS_WITH]->(mark),
(jennifer)-[:IS_FRIENDS_WITH]->(john),
(jennifer)-[:IS_FRIENDS_WITH]->(sally),
(jennifer)-[:IS_FRIENDS_WITH]->(ann),
(john)-[:IS_FRIENDS_WITH]->(sally),
(mark)-[:IS_FRIENDS_WITH]->(ann)
// 查询插入后的数据
MATCH (p:Person)-[r:IS_FRIENDS_WITH]->(friend:Person)
RETURN p.name, friend.name
// 使用COUNT子查询查找有3个或以上朋友的人
MATCH (p:Person)
WHERE COUNT { (p)-[:IS_FRIENDS_WITH]->(:Person) } >= 3
RETURN p.name, COUNT { (p)-[:IS_FRIENDS_WITH]->(:Person) } AS friend_count
// 查询最终结果
MATCH (p:Person)
WHERE COUNT { (p)-[:IS_FRIENDS_WITH]->(:Person) } >= 3
RETURN p.name, COUNT { (p)-[:IS_FRIENDS_WITH]->(:Person) } AS friend_count4. CALL子查询 #
CALL子查询用于执行一个独立的查询,并将结果与主查询合并。它类似于SQL中的子查询。
4.1 前置知识:CALL的作用 #
CALL子查询就像执行一个独立的查询,然后将结果与主查询合并。
4.2 CALL子查询的基本语法 #
CALL {
MATCH ...
RETURN ...
}
RETURN ...4.3 基本CALL用法 #
// 清空数据库
MATCH (n) DETACH DELETE n
// 创建测试数据
CREATE (p1:Person {name: '张三'}),
(p2:Person {name: '李四'}),
(m1:Movie {title: '长津湖'}),
(m2:Movie {title: '满江红'}),
(p1)-[:ACTED_IN]->(m1),
(p2)-[:ACTED_IN]->(m2)
// 查询插入后的数据
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name, m.title
// 使用CALL子查询查找所有演员和电影
CALL {
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name AS name, m.title AS title, 'Actor' AS role
}
RETURN name, title, role
ORDER BY name
// 查询最终结果
CALL {
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
RETURN p.name AS name, m.title AS title, 'Actor' AS role
}
RETURN name, title, role
ORDER BY name5. 小结 #
5.1 核心概念 #
| 概念 | 说明 | 主要用途 |
|---|---|---|
| EXISTS子查询 | 检查是否存在匹配模式 | 模式存在性检查 |
| COUNT子查询 | 计算匹配模式的数量 | 需要知道匹配数量 |
| CALL子查询 | 执行独立的查询 | 组合多个查询结果 |
5.2 最佳实践 #
- 使用EXISTS检查存在性:当只需要知道是否存在时,使用EXISTS比COUNT更高效
- 使用COUNT计算数量:当需要知道具体数量时,使用COUNT
- 使用CALL组合查询:当需要组合多个独立查询的结果时,使用CALL
5.3 常见用法 #
- 使用EXISTS检查模式是否存在
- 使用COUNT计算匹配数量
- 使用CALL组合多个查询结果
- 在WHERE子句中使用子查询进行复杂过滤