精华 Neo4j官方教程:第7节, Cypher 与 SQL 比较
发布于 9 天前 作者 pangguoming 73 次浏览 来自 分享

教程目录 引自:https://neo4j.com/docs/getting-started/cypher-intro/cypher-sql/

Cypher 与 SQL 比较

关系型 vs 图

image ![图 1. Northwind 数据集的关系型和图模型](插入图 1 的链接)

以下查询示例使用 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;

字段访问、排序和分页

您可以过滤掉您感兴趣的属性,而不是返回所有属性,例如 ProductNameUnitPrice

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;

一个较短的选项是使用标签 productNameMATCH 语句中指定产品:

MATCH (p:Product {productName:'Chocolade'})
RETURN p.productName, p.unitPrice;

索引

SQL 和 Cypher 中都提供索引,并且可以更有效地搜索特定的节点标签和属性组合。

Cypher 中的索引仅用于查找查询的起点;所有后续模式匹配都通过图结构完成。Cypher 支持范围、文本、点、查找、全文和向量索引。

在 Northwind 数据集中,在节点标签 productNameunitPrice 上添加索引可以更快地搜索产品及其价格:

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 WITHCONTAINSENDS 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 中使用聚合函数,如 sumcountavgmax

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

其他聚合函数(如 collectpercentileContstdDev)也可用。

员工区域

在 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;
回到顶部