聚合函数

聚合函数对一组值执行计算,并返回单个值。聚合可以在所有匹配路径上进行计算,也可以通过引入分组键 (grouping keys) 进行进一步细分。

要了解更多关于 Cypher® 如何处理针对零行数据执行聚合的信息,请访问 Neo4j 知识库 → 理解针对零行数据的聚合

示例图

以下图表用于下方的示例

要重新创建该图,请在空的 Neo4j 数据库中运行以下查询

CREATE
  (keanu:Person {name: 'Keanu Reeves', age: 58}),
  (liam:Person {name: 'Liam Neeson', age: 70}),
  (carrie:Person {name: 'Carrie Anne Moss', age: 55}),
  (guy:Person {name: 'Guy Pearce', age: 55}),
  (kathryn:Person {name: 'Kathryn Bigelow', age: 71}),
  (speed:Movie {title: 'Speed'}),
  (keanu)-[:ACTED_IN]->(speed),
  (keanu)-[:KNOWS]->(carrie),
  (keanu)-[:KNOWS]->(liam),
  (keanu)-[:KNOWS]->(kathryn),
  (carrie)-[:KNOWS]->(guy),
  (liam)-[:KNOWS]->(guy)

avg()

详细信息

语法

avg(input)

描述

返回一组 INTEGER(整数)、FLOAT(浮点数)或 DURATION(时长)值的平均值。

参数

名称

类型

描述

input

INTEGER | FLOAT | DURATION

聚合以形成平均值的值。

返回

INTEGER | FLOAT | DURATION

注意事项

计算时会排除任何 null 值。

avg(null) 返回 null

示例 1. avg() - 数值
查询
MATCH (p:Person)
RETURN avg(p.age)

返回属性 age 中所有值的平均值

结果
avg(p.age)

61.8

行:1

示例 2. avg() - 时长值
查询
UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur
RETURN avg(dur)

返回提供的两个 DURATION 值的平均值

结果
avg(dur)

P1DT2H22.5S

行:1

collect()

详细信息

语法

collect(input)

描述

返回一个包含表达式所返回值的列表。

参数

名称

类型

描述

input

ANY

聚合到一个列表中的值。

返回

LIST<ANY>

注意事项

任何 null 值都会被忽略,且不会被添加到列表中。

collect(null) 返回一个空列表。

示例 3. collect()
查询
MATCH (p:Person)
RETURN collect(p.age)

所有值被收集并返回在一个单一的列表中

结果
collect(p.age)

[58, 70, 55, 55, 71]

行:1

collect_list()

详细信息

语法

collect_list(input)

描述

返回一个包含表达式所返回值的列表。

参数

名称

类型

描述

input

ANY

聚合到一个列表中的值。

返回

LIST<ANY>

此函数是 collect() 函数的别名,作为 Cypher GQL 一致性的一部分引入。

注意事项

任何 null 值都会被忽略,且不会被添加到列表中。

collect_list(null) 返回一个空列表。

示例 4. collect_list()
查询
MATCH (p:Person)
RETURN collect_list(p.age)

所有值被收集并返回在一个单一的列表中

结果
collect_list(p.age)

[58, 70, 55, 55, 71]

行:1

count()

详细信息

语法

count(input)

描述

返回值的数量或行数。

参数

名称

类型

描述

input

ANY

要聚合的值。

返回

INTEGER(整数)

注意事项

count(*) 包含返回 null 的行。

count(input) 忽略 null 值。

count(null) 返回 0

Neo4j 维护一个用于存储计数元数据的事务性计数存储,这可以显著提高使用 count() 函数的查询速度。有关计数存储的更多信息,请参阅 Neo4j 知识库 → 使用计数存储进行快速计数

使用 count(*) 返回节点数量

函数 count(*) 可用于返回节点数量;例如,与节点 n 连接的节点数量。

示例 5. count()
查询
MATCH (p:Person {name: 'Keanu Reeves'})-->(x)
RETURN labels(p), p.age, count(*)

返回起始节点 Keanu Reeves 的标签和 age 属性,以及与它相关的节点数量

结果
labels(p) p.age count(*)

["Person"]

58

4

行:1

使用 count(*) 对关系类型进行分组和计数

函数 count(*) 可用于对匹配的关系类型进行分组,并返回每种类型的数量。

示例 6. count()
查询
MATCH (p:Person {name: 'Keanu Reeves'})-[r]->()
RETURN type(r), count(*)

对匹配的关系类型进行分组,并返回关系类型的组计数

结果
type(r) count(*)

"ACTED_IN"

1

"KNOWS"

3

行:2

计算非 null

除了简单地使用 count(*) 返回行数外,函数 count(expression) 还可用于返回表达式所返回的非 null 值的数量。

示例 7. count()
查询
MATCH (p:Person)
RETURN count(p.age)

返回带有标签 Person 且具有 age 属性的节点数量:(要计算总和,请使用 sum(n.age)

结果
count(p.age)

5

行:1

计数的去重与不去重

count 函数的默认行为是统计所有匹配结果,包括重复项。要避免统计重复项,请使用 DISTINCT 关键字。

在聚合函数中使用 ALL 关键字也是可能的。这将统计所有结果(包括重复项),在功能上与不使用 DISTINCT 关键字相同。ALL 关键字是作为 Cypher GQL 一致性的一部分引入的。

此示例试图找到 Keanu Reeves 的所有朋友的朋友并统计他们。它展示了使用 ALLDISTINCT 关键字的行为

示例 8. count()
查询
MATCH (p:Person)-->(friend:Person)-->(friendOfFriend:Person)
WHERE p.name = 'Keanu Reeves'
RETURN friendOfFriend.name, count(friendOfFriend), count(ALL friendOfFriend), count(DISTINCT friendOfFriend)

节点 Carrie Anne MossLiam Neeson 都有指向 Guy PearceKNOWS 出站关系。因此,如果不使用 DISTINCTGuy Pearce 节点将被统计两次。

结果
friendOfFriend.name count(friendOfFriend) count(ALL friendOfFriend) count(DISTINCT friendOfFriend)

"Guy Pearce"

2

2

1

行:1

max()

详细信息

语法

max(input)

描述

返回一组值中的最大值。

参数

名称

类型

描述

input

ANY

要聚合的值。

返回

ANY

注意事项

计算时会排除任何 null 值。

在混合集合中,任何数值始终被认为高于任何 STRING 值,任何 STRING 值始终被认为高于任何 LIST<ANY>

列表按字典顺序进行比较,即从列表开头到结尾对元素进行逐对升序比较。

max(null) 返回 null

示例 9. max()
查询
UNWIND [1, 'a', null, 0.2, 'b', '1', '99'] AS val
RETURN max(val)

返回混合集合中所有值的最大值——在此例中为数值 1

结果
max(val)

1

行:1

'99'(一个 STRING)被认为低于 1(一个 INTEGER),因为 '99' 是一个 STRING

示例 10. max()
查询
UNWIND [[1, 'a', 89], [1, 2]] AS val
RETURN max(val)

返回集合中所有列表的最大值——在此例中为列表 [1, 2],因为数字 2 被认为高于 STRING 'a',即使列表 [1, 'a', 89] 包含更多元素。

结果
max(val)

[1,2]

行:1

示例 11. max()
查询
MATCH (p:Person)
RETURN max(p.age)

返回属性 age 中所有值的最大值

结果
max(p.age)

71

行:1

min()

详细信息

语法

min(input)

描述

返回一组值中的最小值。

参数

名称

类型

描述

input

ANY

要聚合的值。

返回

ANY

注意事项

计算时会排除任何 null 值。

在混合集合中,任何 STRING 值始终被认为低于任何数值,任何 LIST<ANY> 始终被认为低于任何 STRING

列表按字典顺序进行比较,即从列表开头到结尾对元素进行逐对升序比较。

min(null) 返回 null

示例 12. min()
查询
UNWIND [1, 'a', null, 0.2, 'b', '1', '99'] AS val
RETURN min(val)

返回混合集合中所有值的最小值——在此例中为 STRING"1"。请注意,(数值)值 0.2 虽然乍看之下似乎是列表中的最小值,但它被认为高于 "1",因为后者是一个 STRING

结果
min(val)

"1"

行:1

示例 13. min()
查询
UNWIND ['d', [1, 2], ['a', 'c', 23]] AS val
RETURN min(val)

返回集合中所有值的最小值——在此例中为列表 ['a', 'c', 23],因为 (i) 这两个列表都被认为低于 STRING "d",且 (ii) STRING "a" 被认为低于数值 1

结果
min(val)

["a","c",23]

行:1

示例 14. min()
查询
MATCH (p:Person)
RETURN min(p.age)

返回属性 age 中所有值的最小值

结果
min(p.age)

55

行:1

percentileCont()

详细信息

语法

percentileCont(input, percentile)

描述

使用线性插值法返回一组值中指定百分位数的数值。

参数

名称

类型

描述

input

FLOAT

要聚合的值。

percentile

FLOAT

0.0 到 1.0 之间的百分位数。

返回

FLOAT

注意事项

计算时会排除任何 null 值。

percentileCont(null, percentile) 返回 null

示例 15. percentileCont()
查询
MATCH (p:Person)
RETURN percentileCont(p.age, 0.4)

返回属性 age 中值的第 40 百分位数,通过加权平均计算得出

结果
percentileCont(p.age, 0.4)

56.8

行:1

percentile_cont()

详细信息

语法

percentile_cont(input, percentile)

描述

使用线性插值法返回一组值中指定百分位数的数值。

参数

名称

类型

描述

input

FLOAT

要聚合的值。

percentile

FLOAT

0.0 到 1.0 之间的百分位数。

返回

FLOAT

此函数是 percentileCont() 函数的别名,作为 Cypher GQL 一致性的一部分引入。

注意事项

计算时会排除任何 null 值。

percentile_cont(null, percentile) 返回 null

示例 16. percentile_cont()
查询
MATCH (p:Person)
RETURN percentile_cont(p.age, 0.4)

返回属性 age 中值的第 40 百分位数,通过加权平均计算得出

结果
percentile_cont(p.age, 0.4)

56.8

行:1

percentileDisc()

详细信息

语法

percentileDisc(input, percentile)

描述

使用舍入法返回一组值中与指定百分位数最接近的 INTEGERFLOAT 值。

参数

名称

类型

描述

input

INTEGER | FLOAT

要聚合的值。

percentile

FLOAT

0.0 到 1.0 之间的百分位数。

返回

INTEGER | FLOAT

注意事项

计算时会排除任何 null 值。

percentileDisc(null, percentile) 返回 null

示例 17. percentileDisc()
查询
MATCH (p:Person)
RETURN percentileDisc(p.age, 0.5)

返回属性 age 中值的第 50 百分位数

结果
percentileDisc(p.age, 0.5)

58

行:1

percentile_disc()

详细信息

语法

percentile_disc(input, percentile)

描述

使用舍入法返回一组值中与指定百分位数最接近的 INTEGERFLOAT 值。

参数

名称

类型

描述

input

INTEGER | FLOAT

要聚合的值。

percentile

FLOAT

0.0 到 1.0 之间的百分位数。

返回

INTEGER | FLOAT

此函数是 percentileDisc() 函数的别名,作为 Cypher GQL 一致性的一部分引入。

注意事项

计算时会排除任何 null 值。

percentile_disc(null, percentile) 返回 null

示例 18. percentile_disc()
查询
MATCH (p:Person)
RETURN percentile_disc(p.age, 0.5)

返回属性 age 中值的第 50 百分位数

结果
percentile_disc(p.age, 0.5)

58

行:1

stDev()

详细信息

语法

stDev(input)

描述

返回给定值在样本总体中的标准差。

参数

名称

类型

描述

input

FLOAT

用于计算标准差的值。

返回

FLOAT

注意事项

计算时会排除任何 null 值。

stDev(null) 返回 0

示例 19. stDev()
查询
MATCH (p:Person)
WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss']
RETURN stDev(p.age)

返回属性 age 中值的标准差

结果
stDev(p.age)

7.937253933193772

行:1

stdev_samp()

详细信息

语法

stdev_samp(input)

描述

返回给定值在样本总体中的标准差。

参数

名称

类型

描述

input

FLOAT

用于计算标准差的值。

返回

FLOAT

此函数是 stDev() 函数的别名,作为 Cypher GQL 一致性的一部分引入。

注意事项

计算时会排除任何 null 值。

stdev_samp(null) 返回 0

示例 20. stdev_samp()
查询
MATCH (p:Person)
WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss']
RETURN stdev_samp(p.age)

返回属性 age 中值的标准差

结果
stdev_samp(p.age)

7.937253933193772

行:1

stDevP()

详细信息

语法

stDevP(input)

描述

返回给定值在整个总体中的标准差。

参数

名称

类型

描述

input

FLOAT

用于计算总体标准差的值。

返回

FLOAT

注意事项

计算时会排除任何 null 值。

stDevP(null) 返回 0

示例 21. stDevP()
查询
MATCH (p:Person)
WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss']
RETURN stDevP(p.age)

返回属性 age 中值的总体标准差

结果
stDevP(p.age)

6.48074069840786

行:1

stdev_pop()

详细信息

语法

stdev_pop(input)

描述

返回给定值在整个总体中的标准差。

参数

名称

类型

描述

input

FLOAT

用于计算总体标准差的值。

返回

FLOAT

此函数是 stDevP() 函数的别名,作为 Cypher GQL 一致性的一部分引入。

注意事项

计算时会排除任何 null 值。

stdev_pop(null) 返回 0

示例 22. stdev_pop()
查询
MATCH (p:Person)
WHERE p.name IN ['Keanu Reeves', 'Liam Neeson', 'Carrie Anne Moss']
RETURN stdev_pop(p.age)

返回属性 age 中值的总体标准差

结果
stdev_pop(p.age)

6.48074069840786

行:1

sum()

详细信息

语法

sum(input)

描述

返回一组 INTEGERFLOATDURATION 值的总和

参数

名称

类型

描述

input

INTEGER | FLOAT | DURATION

要聚合的值。

返回

INTEGER | FLOAT | DURATION

注意事项

计算时会排除任何 null 值。

sum(null) 返回 0

示例 23. sum() - 数值
查询
MATCH (p:Person)
RETURN sum(p.age)

返回属性 age 中所有值的总和

结果
sum(p.age)

309

行:1

示例 24. sum() - 时长值
查询
UNWIND [duration('P2DT3H'), duration('PT1H45S')] AS dur
RETURN sum(dur)

返回提供的两个时长的总和

结果
sum(dur)

P2DT4H45S

行:1

聚合表达式与分组键

聚合表达式是指包含一个或多个聚合函数的表达式。简单的聚合表达式由单个聚合函数组成。例如,sum(x.a) 是一个仅由聚合函数 sum( ) 构成的聚合表达式,其参数为 x.a。聚合表达式也可以更复杂,其中一个或多个聚合函数的结果作为其他表达式的输入参数。例如,0.1 * (sum(x.a) / count(x.b)) 是一个聚合表达式,它包含两个聚合函数:以 x.a 为参数的 sum( ) 和以 x.b 为参数的 count( )。两者都是除法表达式的输入参数。

分组键是用于对输入到聚合函数中的值进行分组的非聚合表达式。例如,在以下包含两个返回表达式 ncount(*) 的查询中:

RETURN n, count(*)

第一个表达式 n 不是聚合函数,因此它将作为分组键。后者 count(*) 是一个聚合函数。匹配的路径将根据分组键被划分为不同的存储桶。然后,聚合函数将在这些存储桶上运行,计算每个存储桶的聚合值。

聚合函数的输入表达式可以包含任何表达式,包括非分组键的表达式。但是,并非所有表达式都能与聚合函数组合。下面的示例将抛出错误,因为 n.x(不是分组键)与聚合函数 count(*) 结合使用了。

RETURN n.x + count(*)

要使用聚合函数对结果集进行排序,聚合必须包含在 RETURN 子句之后的 ORDER BY 子句中。

示例

示例 25. 无分组键的简单聚合
查询
MATCH (p:Person)
RETURN max(p.age)
结果
max(p.age)

71

行:1

示例 26. 聚合与常数的加法,无分组键
查询
MATCH (p:Person)
RETURN max(p.age) + 1
结果
max(p.age) + 1

72

行:1

示例 27. 属性访问与聚合的减法

注意 p 是一个分组键

查询
MATCH (p:Person{name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p, p.age - max(f.age)
结果
p p.age - max(f.age)

{{"name":"Keanu Reeves","age":58}}

-13

行:1

示例 28. 属性访问与聚合的减法。

注意 p.age 是一个分组键

查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p.age, p.age - max(f.age)
结果
p.age p.age - max(f.age)

58

-13

行:1

分组键本身可以是复杂的表达式。为了提高查询的可读性,Cypher 仅在以下情况将聚合表达式中的子表达式识别为分组键:

  • 变量 - 例如 RETURN p, p.age - max(f.age) 中的 p

  • 属性访问 - 例如 RETURN p.age, p.age - max(f.age) 中的 p.age

  • Map 访问 - 例如 WITH {name:'Keanu Reeves', age:58} AS p RETURN p.age, p.age - max(p.age) 中的 p.age

如果需要将更复杂的分组键作为聚合表达式中的操作数,始终可以使用 WITH 提前对它们进行投影。

使用属性 p.age 会抛出异常,因为 p.age 不是一个分组键。因此,它不能用于包含聚合函数的表达式中。以下两个查询会产生相同的错误消息:

查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p.age - max(f.age)
查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
RETURN p.age + p.age, p.age + p.age - max(f.age)
GQLSTATUS 错误链

42I18: 错误: 语法错误或访问规则违规 - 对隐式分组表达式的引用无效。聚合列包含变量 p 引用的隐式分组表达式。隐式分组表达式是指未明确声明为分组键的变量。

42001:错误:语法错误或访问规则冲突 - 无效语法

然而,如果将后者改写如下,查询即可生效:

查询
MATCH (p:Person {name:'Keanu Reeves'})-[:KNOWS]-(f:Person)
WITH p.age + p.age AS groupingKey, f
RETURN groupingKey, groupingKey - max(f.age)
结果
groupingKey groupingKey - max(f.age)

116

45

行:1

聚合表达式规则

为了使聚合表达式能够针对分组键形成的存储桶正确计算,它们必须满足一些要求。具体来说,聚合表达式中的每个子表达式都必须是以下之一:

  • 聚合函数,例如 sum(x.a)

  • 常数,例如 0.1

  • 参数,例如 $param

  • 分组键,例如 RETURN a, count(*) 中的 a

  • 局部变量,例如 count(*) + size([ x IN range(1, 10) | x ]) 中的 x

  • 子表达式,其所有操作数都必须在聚合表达式中是允许的。