ORDER BY

ORDER BY 是一个子句,用于确定 RETURNWITH 子句的结果如何排序。它也可以作为独立子句使用,既可以单独使用,也可以与 SKIP/OFFSETLIMIT 结合使用。

ORDER BY 默认以升序对结果进行排序,但可以修改为以 降序 进行排序。

ORDER BY 依赖于比较来对输出进行排序(详情请参阅 值类型的相等性、排序和比较)。您可以根据不同的值进行排序,例如节点或关系属性、ID 或表达式的结果。

除非使用 ORDER BY,否则 Neo4j 不保证查询结果的行顺序。

示例图

以下示例使用了具有以下模式的图

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

CREATE (o1:Order {id: 'ORD-001', orderDate: datetime('2024-05-01T10:00:00'), total: 550, status: 'shipped'}),
       (o2:Order {id: 'ORD-002', orderDate: datetime('2024-05-02T14:30:00'), total: 1000, status: 'pending'}),
       (o3:Order {id: 'ORD-003', orderDate: datetime('2024-05-03T09:15:00'), total: 550, status: 'pending'}),
       (o4:Order {id: 'ORD-004', orderDate: datetime('2024-05-04T12:45:00'), total: 200}),
       (o5:Order {id: 'ORD-005', orderDate: datetime('2024-05-05T15:00:00'), total: 800, status: 'shipped'}),

       (i1:Item {name: 'Phone', price: 500}),
       (i2:Item {name: 'Laptop', price: 1000}),
       (i3:Item {name: 'Headphones', price: 250}),
       (i4:Item {name: 'Charger', price: 50}),
       (i5:Item {name: 'Keyboard', price: 200}),

       (o1)-[:CONTAINS]->(i1),
       (o1)-[:CONTAINS]->(i4),
       (o2)-[:CONTAINS]->(i2),
       (o3)-[:CONTAINS]->(i1),
       (o3)-[:CONTAINS]->(i4),
       (o4)-[:CONTAINS]->(i5),
       (o5)-[:CONTAINS]->(i1),
       (o5)-[:CONTAINS]->(i3),
       (o5)-[:CONTAINS]->(i4)

基本示例

示例 1. 按属性值排序

ORDER BY 可用于按属性值对结果进行排序。

按节点属性排序
MATCH (o:Order)
RETURN o.id AS order,
       o.total AS total
  ORDER BY total

返回的节点按照 total 属性的值进行升序排列。

结果
order 总计

"ORD-004"

200

"ORD-001"

550

"ORD-003"

550

"ORD-005"

800

"ORD-002"

1000

行:5

示例 2. 按多个属性值排序

通过在 ORDER BY 子句中列出两个或多个属性,可以按多个属性值进行排序。Cypher® 会首先按第一个属性排序,如果值相等,则进入下一个属性,依此类推。

按多个属性排序
MATCH (o:Order)
RETURN o.id AS order,
       o.total AS total,
       o.orderDate AS orderDate
  ORDER BY total,
           orderDate

这将返回节点,首先按它们的 total 属性排序,如果值相等,则按它们的 orderDate 属性排序。

结果
order 总计 orderDate

"ORD-004"

200

2024-05-04T12:45Z

"ORD-001"

550

2024-05-01T10:00Z

"ORD-003"

550

2024-05-03T09:15Z

"ORD-005"

800

2024-05-05T15:00Z

"ORD-002"

1000

2024-05-02T14:30Z

行:5

示例 3. 按 ID 排序

ORDER BY 可用于按节点或关系的 ID 对其进行排序(通过 elementId()id() 函数获取)。

按元素 ID 排序
MATCH (o:Order)
RETURN o.id AS order,
       elementId(o) AS elementId
  ORDER BY elementId
结果
order elementId

"ORD-001"

"4:9350eddd-5a35-413d-8684-708b1da35d23:0"

"ORD-002"

"4:9350eddd-5a35-413d-8684-708b1da35d23:1"

"ORD-003"

"4:9350eddd-5a35-413d-8684-708b1da35d23:2"

"ORD-004"

"4:9350eddd-5a35-413d-8684-708b1da35d23:3"

"ORD-005"

"4:9350eddd-5a35-413d-8684-708b1da35d23:4"

行:5

当节点和关系被删除时,Neo4j 会重用其内部 ID。因此,依赖内部 Neo4j ID 的应用程序非常脆弱,且可能导致不准确。建议改用应用程序生成的 ID。
示例 4. 按表达式排序

ORDER BY 可用于根据 表达式 的结果进行排序。以下查询计算每个订单 total 属性值的 10% 折扣,然后按折扣后的总额对结果进行排序。

按表达式结果排序
MATCH (o:Order)
RETURN o.id AS order,
       o.total * 0.9 AS discountedTotal
  ORDER BY discountedTotal
结果
order discountedTotal

"ORD-004"

180.0

"ORD-001"

495.0

"ORD-003"

495.0

"ORD-005"

720.0

"ORD-002"

900.0

行:5

接下来的这个查询 计算 了每个订单中包含的项数,然后按项数对结果进行排序。

按表达式结果排序
MATCH (o:Order)
RETURN o.id AS order,
      COUNT { (o)-[:CONTAINS]->(:Item) } AS itemCount
  ORDER BY itemCount
结果
order itemCount

"ORD-002"

1

"ORD-004"

1

"ORD-001"

2

"ORD-003"

2

"ORD-005"

3

行:5

按结果中不包含的值排序

ORDER BY 可以按结果集中未包含的值进行排序。也就是说,排序键不必是先前 RETURNWITH 子句的一部分。例如,下面的查询根据订单包含的项数对订单进行排序,即使该计数并未返回。

按返回结果中不包含的值排序
MATCH (o:Order)
RETURN o.id AS order
  ORDER BY COUNT { (o)-[:CONTAINS]->(:Item) }
结果
order

"ORD-002"

"ORD-004"

"ORD-001"

"ORD-003"

"ORD-005"

行:5

升序和降序

ORDER BY 默认按升序对结果进行排序。若要显式按升序排序,请附加 ASC[ENDING]

显式按升序对结果排序
MATCH (o:Order)
RETURN o.id AS order,
       o.total AS total
  ORDER BY total ASC
结果
order 总计

"ORD-004"

200

"ORD-001"

550

"ORD-003"

550

"ORD-005"

800

"ORD-002"

1000

行:5

若要按降序对结果进行排序,请附加 DESC[ENDING]

按降序对结果排序
MATCH (o:Order)
RETURN o.id AS order,
       o.total AS total
  ORDER BY total DESC
结果
order 总计

"ORD-002"

1000

"ORD-005"

800

"ORD-001"

550

"ORD-003"

550

"ORD-004"

200

行:5

ORDER BY 可以组合升序和降序。在下面的示例中,结果首先按 total 值降序排序,然后按 orderDate 值升序排序。

组合升序和降序结果排序
MATCH (o:Order)
RETURN o.id AS order,
       o.total AS total,
       o.orderDate AS orderDate
  ORDER BY total DESC,
           orderDate ASC
结果
order 总计 orderDate

"ORD-002"

1000

2024-05-02T14:30Z

"ORD-005"

800

2024-05-05T15:00Z

"ORD-001"

550

2024-05-01T10:00Z

"ORD-003"

550

2024-05-03T09:15Z

"ORD-004"

200

2024-05-04T12:45Z

行:5

ORDER BY 和模式匹配

ORDER BY 可用于在继续进行其他模式匹配之前对结果进行排序。在下面的示例中,它与 LIMIT 结合使用,首先按 Order 节点的 orderDate 属性值对它们进行排序,将结果限制为最近的 Order,然后匹配任何连接的 Item 节点。另请注意,在本例中,ORDER BYLIMIT 是作为 独立子句 使用的,而不是作为子句中的子句。

查找最近下的订单中包含的项
MATCH (o:Order)
ORDER BY o.orderDate DESC
LIMIT 1
MATCH (o)-[:CONTAINS]->(i:Item)
RETURN o.id AS order,
       o.total,
       collect(i.name) AS items
结果
order 总计 items

"ORD-005"

800

["Phone", "Headphones", "Charger"]

行:1

Null 值

排序时,null 值在升序中排在最后,在降序中排在最前。

按 null 属性排序
MATCH (o:Order)
RETURN o.id AS order,
       o.status AS status
  ORDER BY status DESC
结果
order 状态 (status)

"ORD-004"

null

"ORD-001"

"shipped"

"ORD-005"

"shipped"

"ORD-002"

"pending"

"ORD-003"

"pending"

行:5

ORDER BY 和 WITH 子句

ORDER BY 出现在 WITH 子句中时,紧随其后的子句将按指定的顺序接收记录。这种保证的顺序对于依赖处理顺序的操作很有用。例如,将 ORDER BY 附加到 WITH 子句可用于控制 collect() 聚合函数产生的列表中项的顺序。MERGESET 子句也具有可以通过这种方式控制的排序依赖性。

下面的示例使用 WITHORDER BYItem 节点的 price 属性对其进行排序,然后随后的 RETURN 子句中的 collect() 基于该排序为每个订单构建一个有序列表。

WITH, ORDER BYcollect()
MATCH (o:Order)-[:CONTAINS]->(i:Item)
WITH o, i
  ORDER BY i.price DESC
RETURN o.id AS order,
       collect(i.name || " ($" || toString(i.price) || ")") AS orderedListOfItems
结果
order orderedListOfItems

"ORD-002"

["Laptop ($1000)"]

"ORD-001"

["Phone ($500)", "Charger ($50)"]

"ORD-003"

["Phone ($500)", "Charger ($50)"]

"ORD-005"

["Phone ($500)", "Headphones ($250)", "Charger ($50)"]

"ORD-004"

["Keyboard ($200)"]

行:5

对聚合或 DISTINCT 结果进行排序

ORDER BY 可用的变量取决于前面的 RETURNWITH 子句是否执行聚合来组合结果,或者是否使用 DISTINCT 来去除重复项。

  • 如果 RETURNWITH 不聚合值或不使用 DISTINCT,则 ORDER BY 可以引用前面 RETURNWITH 子句中引用的任何变量。

在排除聚合或 DISTINCTWITH 子句之后的 ORDER BY
MATCH (o:Order)-[:CONTAINS]->(i:Item)
WITH o.id AS order,
     i.name AS item
  ORDER BY o.orderDate
RETURN order, item
  • 如果 RETURNWITH 执行聚合或使用 DISTINCT,则只有来自任一操作的投影变量才可供 ORDER BY 使用。这是因为这些操作会更改子句产生的行数,而任何未显式投影的变量都会被丢弃。

在投影聚合值的 WITH 子句之后的 ORDER BY
MATCH (o:Order)-[:CONTAINS]->(i:Item)
WITH collect(o.id) AS orders,
     i.name AS items
  ORDER BY o.orderDate
RETURN orders, items
GQLSTATUS 错误链

42N44: error: 语法错误或访问规则冲突 - 无法访问的变量。当使用 DISTINCT 或聚合时,无法访问在 WITH 子句之前声明的变量 o

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

ORDER BY 和索引

使用 ORDER BY 对节点属性进行排序的 Cypher 查询性能可能会受到索引存在及其用于查找节点的影响。如果索引能够按照查询请求的顺序提供节点,Cypher 可以避免使用昂贵的 Sort 操作。请在 范围索引支持的 ORDER BY 中阅读有关此功能的更多信息。

ORDER BY 用作独立子句

ORDER BY 可以用作独立子句,也可以与 SKIP/OFFSETLIMIT 结合使用。

ORDER BY 的独立使用
MATCH (i:Item)
ORDER BY i.price
RETURN collect(i.name || " ($" || toString(i.price) || ")") AS orderedPriceList
结果
orderedPriceList

["Charger ($50)", "Keyboard ($200)", "Headphones ($250)", "Phone ($500)", "Laptop ($1000)"]

行:1

ORDER BYSKIPLIMIT 结合使用
MATCH (i:Item)
ORDER BY i.price DESC
SKIP 1
LIMIT 1
RETURN i.name AS secondMostExpensiveItem,
       i.price AS price
结果
secondMostExpensiveItem 价格

"Phone"

500

行:1