教程目录 引自:https://neo4j.com/docs/getting-started/cypher-intro/cypher-sql/
Cypher 与 SQL 比较
关系型 vs 图

以下查询示例使用 Northwind 数据集。它们旨在帮助熟悉 SQL 的人理解 Cypher® 并编写等效于 SQL 的 Cypher 查询。有关数据集中关系型和图模型之间差异的可视化表示,请参见图 1。
请参阅示例数据集以了解如何将 Northwind 加载到您的 Neo4j 实例并尝试这些示例。
有关图数据库和关系型数据库之间差异和相似之处的更深入解释,请参见从关系型数据库过渡到图数据库。
查询示例
选择和返回记录
SQL
要选择和返回 SQL 中的记录,请从 products 表中选择所有内容:
SELECT p.*
FROM products as p;
Cypher
在 Cypher 中,您 MATCH
一个简单的模式:所有带有标签 :Product
的节点,并 RETURN
它们:
MATCH (p:Product)
RETURN p;
字段访问、排序和分页
您可以过滤掉您感兴趣的属性,而不是返回所有属性,例如 ProductName
和 UnitPrice
。
SQL
在 SQL 中,这是您按价格对项目进行排序并返回 10 个最昂贵的项目的方式:
SELECT p.ProductName, p.UnitPrice
FROM products as p
ORDER BY p.UnitPrice DESC
LIMIT 10;
Cypher
该语句在 Cypher 中类似,除了模式匹配部分:
MATCH (p:Product)
RETURN p.productName, p.unitPrice
ORDER BY p.unitPrice DESC
LIMIT 10;
请记住,标签、关系类型和属性名称在 Neo4j 中区分大小写。有关命名规则的更多详细信息,请参见 [Cypher 手册 → 命名规则和建议](链接到 Cypher 手册 → 命名规则和建议)。
按名称查找单个产品
有不同的方法来查询数据库并检索单个项目,例如,名为 Chocolade 的产品。
按相等性过滤
SQL
在 SQL 中,您可以使用 WHERE
子句过滤数据:
SELECT p.ProductName, p.UnitPrice
FROM products AS p
WHERE p.ProductName = 'Chocolade';
Cypher
在 Cypher 中,WHERE
子句属于 MATCH
语句:
MATCH (p:Product)
WHERE p.productName = 'Chocolade'
RETURN p.productName, p.unitPrice;
一个较短的选项是使用标签 productName
在 MATCH
语句中指定产品:
MATCH (p:Product {productName:'Chocolade'})
RETURN p.productName, p.unitPrice;
索引
SQL 和 Cypher 中都提供索引,并且可以更有效地搜索特定的节点标签和属性组合。
Cypher 中的索引仅用于查找查询的起点;所有后续模式匹配都通过图结构完成。Cypher 支持范围、文本、点、查找、全文和向量索引。
在 Northwind 数据集中,在节点标签 productName
和 unitPrice
上添加索引可以更快地搜索产品及其价格:
CREATE INDEX Product_productName IF NOT EXISTS FOR (p:Product) ON p.productName;
CREATE INDEX Product_unitPrice IF NOT EXISTS FOR (p:Product) ON p.unitPrice;
Cypher 中的索引仅用于查找查询的起点。所有后续模式匹配都通过图结构完成。Cypher 支持范围、文本、点、查找、全文和向量索引。阅读有关如何在 [Cypher 手册 → 使用索引](链接到 Cypher 手册 → 使用索引) 中使用索引的更多信息。
过滤产品
有几种方法可以在 Cypher 中过滤结果,这些方法与 SQL 类似。
按列表/范围过滤
SQL
在 SQL 中,您可以使用运算符 IN
:
SELECT p.ProductName, p.UnitPrice
FROM products as p
WHERE p.ProductName IN ('Chocolade','Chai');
Cypher
Cypher 具有完整的集合支持,包括 IN
和其他集合函数、谓词和转换:
MATCH (p:Product)
WHERE p.productName IN ['Chocolade','Chai']
RETURN p.productName, p.unitPrice;
按多个数字和文本谓词过滤
SQL
此查询检索名称以 “C” 开头且价格大于 100 的产品:
SELECT p.ProductName, p.UnitPrice
FROM products AS p
WHERE p.ProductName LIKE 'C%' AND p.UnitPrice > 100;
Cypher
在 Cypher 中,LIKE
运算符被 STARTS WITH
、CONTAINS
和 ENDS WITH
运算符替换:
MATCH (p:Product)
WHERE p.productName STARTS WITH 'C' AND p.unitPrice > 100
RETURN p.productName, p.unitPrice;
您也可以使用正则表达式:
MATCH (p:Product)
WHERE p.productName =~ '^C.*'
RETURN p.productName, p.unitPrice
将产品与客户连接
SQL
在 SQL 中,如果您想查看谁购买了 Chocolade,您可以将四个表连接在一起:
SELECT DISTINCT c.CompanyName
FROM customers AS c
JOIN orders AS o ON (c.CustomerID = o.CustomerID)
JOIN order_details AS od ON (o.OrderID = od.OrderID)
JOIN products AS p ON (od.ProductID = p.ProductID)
WHERE p.ProductName = 'Chocolade';
Cypher
在 Cypher 中,无需 JOIN
表。您可以将连接表示为图模式:
MATCH (p:Product {productName:'Chocolade'})<-[:ORDERS]-(:Order)<-[:PURCHASED]-(c:Customer)
RETURN DISTINCT c.companyName;
返回没有现有订单的客户
如果您想查看谁购买了什么以及他们总共支付了多少,则先前 SQL 查询中的 JOIN
保持不变,只有过滤表达式发生变化。但是,如果您有没有任何订单的客户并且仍然想返回他们,则需要进行一些调整。
SQL
在 SQL 中,您必须使用 OUTER JOINS
来确保即使其他表中没有匹配的行也返回结果:
SELECT p.ProductName, sum(od.UnitPrice * od.Quantity) AS Volume
FROM customers AS c
LEFT OUTER JOIN orders AS o ON (c.CustomerID = o.CustomerID)
LEFT OUTER JOIN order_details AS od ON (o.OrderID = od.OrderID)
LEFT OUTER JOIN products AS p ON (od.ProductID = p.ProductID)
WHERE c.CompanyName = 'Drachenblut Delikatessen'
GROUP BY p.ProductName
ORDER BY Volume DESC;
Cypher
在 Cypher 中,客户和订单之间的 MATCH
变为 OPTIONAL MATCH
,这等效于 OUTER JOIN
:
MATCH (c:Customer {companyName:'Drachenblut Delikatessen'})
OPTIONAL MATCH (p:Product)<-[o:ORDERS]-(:Order)<-[:PURCHASED]-(c)
RETURN p.productName, toInteger(sum(o.unitPrice * o.quantity)) AS volume
ORDER BY volume DESC;
不存在的节点和关系将具有空值,这将导致属性为空,并且不会被 sum
聚合。
畅销员工
前面的示例提到了聚合。通过对产品价格和订购数量求和,提供了每个客户的每个产品的聚合视图。
您可以在 SQL 和 Cypher 中使用聚合函数,如 sum
、count
、avg
和 max
。
SQL
在 SQL 中,聚合是显式的,因此您必须在 GROUP BY
子句中再次提供所有分组键。要查看畅销员工,请运行以下查询:
SELECT e.EmployeeID, e.FirstName, e.LastName, COUNT(*) AS Count
FROM Employee AS e
JOIN Orders AS o ON (o.EmployeeID = e.EmployeeID)
GROUP BY e.EmployeeID, e.FirstName, e.LastName
ORDER BY Count DESC
LIMIT 10;
Cypher
在 Cypher 中,聚合的分组是隐式的。只要您使用第一个聚合函数,所有非聚合列都会自动成为分组键:
MATCH (:Order)<-[:SOLD]-(e:Employee)
WITH e, count(*) as cnt
ORDER BY cnt DESC LIMIT 10
RETURN e.employeeID, e.firstName, e.lastName, cnt
其他聚合函数(如 collect
、percentileCont
、stdDev
)也可用。
员工区域
在 SQL 中,处理主详细信息可能具有挑战性。一个示例是当您有一个主要实体(主实体、头实体、父实体)和许多依赖实体(详细信息、位置、子实体)时。
您可以编写一个连接两者并多次返回主数据的查询(每个详细信息一次),或者仅获取主键,然后通过该外键拉取所有详细信息行。
SQL
在 SQL 中,如果您查看每个区域的员工,则会为每个员工返回区域信息:
SELECT e.LastName, et.Description
FROM Employee AS e
JOIN EmployeeTerritory AS et ON (et.EmployeeID = e.EmployeeID)
JOIN Territory AS t ON (et.TerritoryID = t.TerritoryID);
Cypher
在 Cypher 中,您可以像在 SQL 中一样返回结构,也可以使用 collect()
聚合函数,该函数将值聚合到集合(列表、数组)中。这样,每个父级仅返回一行,其中包含子值的内联集合:
MATCH (t:Territory)<-[:IN_TERRITORY]-(e:Employee)
RETURN t.territoryDescription, collect(e.lastName);
这也适用于嵌套值。
产品类别
如果您必须在 SQL 中表达类别、区域或组织层次结构,通常使用从子级到父级的外键通过自连接进行建模。在产品类别的示例中,您必须预先决定要查询多少级别的类别。
SQL
此处仅显示三个潜在级别(这意味着 ProductCategory
表的 1+2+3 = 6 个自连接):
SELECT p.ProductName
FROM Product AS p
JOIN ProductCategory pc ON (p.CategoryID = pc.CategoryID AND pc.CategoryName = "Dairy Products")
JOIN ProductCategory pc1 ON (p.CategoryID = pc1.CategoryID)
JOIN ProductCategory pc2 ON (pc2.ParentID = pc2.CategoryID AND pc2.CategoryName = "Dairy Products")
JOIN ProductCategory pc3 ON (p.CategoryID = pc3.CategoryID)
JOIN ProductCategory pc4 ON (pc3.ParentID = pc4.CategoryID)
JOIN ProductCategory pc5 ON (pc4.ParentID = pc5.CategoryID AND pc5.CategoryName = "Dairy Products")
;
Cypher
Cypher 能够仅使用适当的关系来表达任何深度的层次结构。可变级别由可变长度路径表示,这些路径由关系类型后的星号 *
和可选限制 (min..max
) 表示:
MATCH (p:Product)-[:PART_OF]->(l:Category)-[:PARENT*0..]->(:Category {name:'Dairy Products'})
RETURN p.name;