通过图形查询遍历关系

已完成

某些数据关系自然地表示为网络,包括社交连接、组织层次结构、产品建议和欺诈检测模式。 虽然可以使用外键和联接对这些关系进行建模,但使用运算符的 MATCH 图形查询提供了更直观、更高效的方式来遍历连接的数据。

可视化图形数据结构

在编写图形查询之前,它有助于直观显示图形数据的组织方式。 考虑一个简单的社交网络,人们相互了解并购买产品:

显示图形数据模型的图表,其中“人员”和“产品”节点通过“认识”和“购买”边连接。

在此模型中:

  • 节点 (框)表示人员和产品等实体
  • 边缘 (箭头)表示节点之间的关系。 箭头方向指示关系方向(Alice 知道 Bob,不一定 Bob 知道 Alice)。

注释

此图说明了图形概念。 本单元中的代码示例使用类似但简化的数据来专注于特定功能。

了解图形功能

图形功能使用专用节点和边缘表扩展关系模型。 节点表示人员、产品和位置等实体。 边表示它们之间的关系,例如“认识”“购买”或“位于”。

图形查询的主要优点是模式匹配。 而不是编写复杂的多向联接,您可以使用 ASCII 样式语法来表达所寻找的模式:

-- Traditional relational approach (multiple joins)
SELECT p1.Name, p2.Name
FROM Person AS p1
INNER JOIN Friendship AS f ON p1.PersonID = f.Person1ID
INNER JOIN Person AS p2 ON f.Person2ID = p2.PersonID;

-- Graph approach (pattern matching)
SELECT Person1.Name, Person2.Name
FROM Person AS Person1, Friendship, Person AS Person2
WHERE MATCH(Person1-(Friendship)->Person2);

注释

图形表与现有关系功能完全兼容。 可以将图形表与常规表联接,使用索引并应用所有标准 T-SQL作。

创建节点表

节点表将实体存储在图形中。 使用CREATE TABLE并通过AS NODE子句来创建它们:

-- Create a Person node table
CREATE TABLE dbo.Person (
    PersonID INT PRIMARY KEY,
    Name NVARCHAR(100) NOT NULL,
    Email NVARCHAR(200),
    Department NVARCHAR(50)
) AS NODE;

-- Create a Product node table
CREATE TABLE dbo.Product (
    ProductID INT PRIMARY KEY,
    Name NVARCHAR(200) NOT NULL,
    Category NVARCHAR(100),
    Price DECIMAL(10, 2)
) AS NODE;

-- Create a Location node table
CREATE TABLE dbo.Location (
    LocationID INT PRIMARY KEY,
    City NVARCHAR(100) NOT NULL,
    CountryRegion NVARCHAR(100) NOT NULL
) AS NODE;

SQL Server 会自动向唯一标识每个节点的节点表添加一列 $node_id 。 系统在内部将此列用于图形关系。

下面的示例将四个人插入到 Person 节点表中,然后查询该表以显示业务列和系统生成的 $node_id表。 请注意,INSERT 语句仅使用用户定义的列。 SQL Server 为每个行自动生成 $node_id

-- Insert person data using standard INSERT syntax
INSERT INTO dbo.Person (PersonID, Name, Email, Department)
VALUES 
    (1, 'Alice Johnson', 'alice@contoso.com', 'Engineering'),
    (2, 'Bob Smith', 'bob@contoso.com', 'Marketing'),
    (3, 'Carol Davis', 'carol@contoso.com', 'Engineering'),
    (4, 'David Lee', 'david@contoso.com', 'Sales');

-- Query shows the system-generated $node_id alongside user columns
SELECT $node_id, PersonID, Name FROM dbo.Person;

创建边缘表

边缘表表示节点之间的关系。 使用CREATE TABLE并结合AS EDGE子句来创建它们:

-- Create a "reports to" relationship edge
CREATE TABLE dbo.ReportsTo (
    StartDate DATE,
    ReportType NVARCHAR(50)
) AS EDGE;

-- Create a "purchased" relationship edge
CREATE TABLE dbo.Purchased (
    PurchaseDate DATE NOT NULL,
    Quantity INT NOT NULL,
    TotalAmount DECIMAL(10, 2)
) AS EDGE;

-- Create a "knows" relationship edge (social connection)
CREATE TABLE dbo.Knows (
    ConnectionDate DATE,
    ConnectionStrength INT  -- 1-10 scale
) AS EDGE;

SQL Server 会自动向边缘表添加$edge_id$from_id列和$to_id列。 可以通过指定连接节点的 $from_id$to_id 值来插入边,如下所示:

-- Alice reports to Bob
INSERT INTO dbo.ReportsTo ($from_id, $to_id, StartDate, ReportType)
SELECT 
    (SELECT $node_id FROM dbo.Person WHERE Name = 'Alice Johnson'),
    (SELECT $node_id FROM dbo.Person WHERE Name = 'Bob Smith'),
    '2023-01-15',
    'Direct';

-- Create social connections
INSERT INTO dbo.Knows ($from_id, $to_id, ConnectionDate, ConnectionStrength)
SELECT 
    (SELECT $node_id FROM dbo.Person WHERE Name = 'Alice Johnson'),
    (SELECT $node_id FROM dbo.Person WHERE Name = 'Carol Davis'),
    '2022-06-01',
    8;

小窍门

边缘表可以存储关系本身的属性,例如日期、权重或类型。 这对于时态分析或加权图算法非常有用。

使用 MATCH 子句查询图形

MATCH 子句使用模式语法来指定要查找的关系。 基本模式使用箭头显示关系方向:

-- Find who reports to whom
SELECT 
    Employee.Name AS Employee,
    Manager.Name AS Manager,
    r.StartDate
FROM dbo.Person AS Employee, 
     dbo.ReportsTo AS r, 
     dbo.Person AS Manager
WHERE MATCH(Employee-(r)->Manager);

箭头方向很重要:

  • (Node1)-(Edge)->(Node2):Edge 从 Node1 到 Node2
  • (Node1)<-(Edge)-(Node2):Edge 从 Node2 到 Node1

以下示例查找所有认识 Alice 的人:

SELECT 
    Connector.Name AS PersonWhoKnowsAlice,
    k.ConnectionStrength
FROM dbo.Person AS Connector, 
     dbo.Knows AS k, 
     dbo.Person AS Target
WHERE MATCH(Connector-(k)->Target)
  AND Target.Name = 'Alice Johnson';

遍历多个关系

单跃点查询查找直接连接,但图形数据库在多跃点遍历中表现出色。 可以在单个 MATCH 子句中链接多个边模式,以遵循多个关系的路径。 此功能允许你回答诸如“我的朋友是谁?”或“我的同事购买哪些产品?”之类的问题,而无需编写复杂的嵌套子查询。

以下示例通过链接两个 KNOWS 关系来查找好友。 模式 Person1-(k1)->Person2-(k2)->Person3 从 Person1 开始,沿着一条 KNOWS 边到 Person2,然后沿着另一条 KNOWS 边到达 Person3:

-- Find friends of friends (2-hop connections)
SELECT DISTINCT
    Person1.Name AS Person,
    Person3.Name AS FriendOfFriend
FROM dbo.Person AS Person1,
     dbo.Knows AS k1,
     dbo.Person AS Person2,
     dbo.Knows AS k2,
     dbo.Person AS Person3
WHERE MATCH(Person1-(k1)->Person2-(k2)->Person3)
  AND Person1.Name = 'Alice Johnson'
  AND Person3.Name <> Person1.Name;  -- Exclude self

还可以在单个遍历中组合不同的关系类型。 以下示例从 KNOWS 边跨到 PURCHASED 边,查找给定人员认识的人购买了哪些产品:

-- Find products purchased by people in the same department
SELECT DISTINCT
    p1.Name AS Person,
    p1.Department,
    prod.Name AS Product
FROM dbo.Person AS p1,
     dbo.Knows AS k,
     dbo.Person AS p2,
     dbo.Purchased AS pu,
     dbo.Product AS prod
WHERE MATCH(p1-(k)->p2-(pu)->prod)
  AND p1.Department = p2.Department;

重要

每个边缘表别名只能在单个 MATCH 模式中显示一次。 若要多次遍历同一边缘类型,请使用单独的别名。

请使用 SHORTEST_PATH 进行可变长度遍历

可用于 SHORTEST_PATH 查找跨可变关系数的最短连接。 关键字 FOR PATH 标记参与可变长度匹配的表,而类似 + (一个或多个)或 {1,3} (一到三)的量词控制遍历深度。

以下示例查找从 Alice 出发三跳内可达的所有人,并计算到每个人的距离:

SELECT 
    StartPerson.Name,
    LAST_VALUE(ReachablePerson.Name) WITHIN GROUP (GRAPH PATH) AS ReachablePerson,
    COUNT(ReachablePerson.Name) WITHIN GROUP (GRAPH PATH) AS Distance
FROM dbo.Person AS StartPerson,
     dbo.Knows FOR PATH AS k,
     dbo.Person FOR PATH AS ReachablePerson
WHERE MATCH(SHORTEST_PATH(StartPerson(-(k)->ReachablePerson){1,3}))
  AND StartPerson.Name = 'Alice Johnson';

在图形和关系方法之间进行选择

图形查询并不总是最佳选择。 在关系图和传统关系方法之间做出决定时,请考虑以下准则:

在以下情况下使用图形查询:

  • 关系是查询的主要焦点
  • 需要遍历可变或未知深度(朋友的朋友的朋友)
  • 数据自然形成网络(社交图、层次结构、路由)
  • 查询模式在使用关系 SQL 时需要进行许多自联接
  • 正在执行路径查找或连接分析

在以下情况下使用关系查询:

  • 关系简单且深度固定(父子级结构具有一个级别)
  • 主要是筛选和聚合实体属性
  • 数据模型大多是表格形式,关系很少
  • 性能至关重要,且外键上的索引已足够
  • 团队更熟悉传统的 SQL 模式

排查常见图形查询难题

图形查询具有唯一的语法要求,可能会导致错误。 下表介绍了常见的难题以及如何解决这些问题。

挑战 原因 解决方案
查询不返回任何结果 图案中的 MATCH 箭头方向与插入边缘的方式不匹配 验证如何插入边。 如果 $from_id 为 Employee 且 $to_id 是 Manager,则箭头必须从员工指向经理。
重复边的语法错误 同一 MATCH 模式中多次使用相同的边别名 为相同边缘类型的每个遍历创建单独的别名。
SHORTEST_PATH 查询失败 未标记为 FOR PATH 的边缘和节点表。 FOR PATH 关键字添加到参与可变长度匹配的所有表。
边引用不存在的节点 业务键列被用来替代 $node_id 插入边缘时,使用子查询从节点表中进行选择 $node_id

注释

图形表和 MATCH 运算符在 SQL Server 2017 及更高版本中可用,以及 Azure SQL 数据库。 该 SHORTEST_PATH 函数需要 SQL Server 2019 或更高版本。 查看平台的文档以获取特定功能可用性。

有关图形功能的详细信息,请参阅 使用 SQL Server 和MATCH (Transact-SQL) 进行图形处理。