sql - 使用标准 SQL 模拟图形查询

标签 sql sql-server graph cypher

我想知道是否有任何方法可以使用直接 SQL 来模拟图形模式匹配,或许可以借助递归 CTE。

这是使用 MATCH Cypher-like syntax in SQL Server 的示例:

Find 2 people who are both friends with same person

SELECT Person1.name AS Friend1, Person2.name AS Friend2
FROM Person Person1, friend friend1, Person Person2, 
        friend friend2, Person Person0
WHERE MATCH(Person1-(friend1)->Person0<-(friend2)-Person2);

我知道这可以通过进行一堆连接来完成,但我想知道是否有另一种方法可以在这种简洁的模式中完成它,也许使用递归 CTE(无需访问 SQL-Server MATCH子句)。


这是一个更新的示例,尽管需要执行几个步骤才能导入它:

模拟查询“每个供应商供应哪些类别的食品?”

MATCH (s:Supplier)-->(:Product)-->(c:Category)
RETURN s.companyName as Company, collect(distinct c.categoryName) as Categories

或者,如果表达得更像sql:

SELECT Company, CategoryName, Categories
FROM table
MATCH (s:Supplier)-->(:Product)-->(c:Category)

这取自 Neo4j 示例 -- https://neo4j.com/developer/guide-importing-data-and-etl/ .

基本上,我不想对所有 JOIN 进行硬编码(我知道该怎么做),而是想创建类似函数或表函数之类的东西,可以将其称为:

SELECT Field1, Field2, ...
FROM MATCH_TABLE_FUNCTION(table.field1, {'-->'|'--','<--'}, table.field2, ...})

我知道这不是一个简单的问题,但希望我们能在这里得到一些有创意的答案。

最佳答案

假设表中有这些数据:

create table #Person (id integer primary key, name varchar(50));
create table #friend (friend1 int, friend2 int, start_date date);

-- Insert into node table
insert into #Person values (1, 'Alice');
insert into #Person values (2, 'John');
insert into #Person values (3, 'Jacob');
insert into #Person values (4, 'Bob');
insert into #Person values (5, 'Dave');
insert into #Person values (6, 'Charlie');
insert into #Person values (7, 'Etan');
insert into #Person values (8, 'Fred');
insert into #Person values (9, 'Garry');
insert into #Person values (10, 'Hanna');


set dateformat ymd
-- Insert into edge table
insert into #friend (friend1, friend2, start_date) values
(1, 6, '2008-05-09'),
(2, 8, '2002-07-10'),
(2, 6, '2011-11-25'),
(2, 10, '2001-11-18'),
(3, 5, '2000-02-20'),
(3, 8, '2006-10-28'),
(4, 6, '2007-06-11'),
(4, 5, '2016-05-24'),
(4, 10, '2015-11-13'),
(5, 10, '2017-02-21'),
(5, 1, '2011-02-18'),
(5, 2, '2015-08-24'),
(6, 3, '2011-06-09'),
(6, 7, '2003-09-10'),
(6, 8, '2009-07-10'),
(7, 5, '2007-02-06'),
(7, 3, '2000-02-07'),
(8, 7, '2013-04-28'),
(9, 4, '2006-08-17'),
(9, 3, '2007-01-22'),
(9, 1, '2011-06-02'),
(9, 5, '2007-08-16'),
(10, 9, '2006-12-09')

因此,CTE 解决方案可能如下所示:

;with friendship as 
(
    select f1.friend1, f1.friend2 common_friend, 1 as level, 0 friend2, 0 common_friend2
        from #friend f1
    union all
    select f1.friend1, f1.common_friend, level+1, f2.friend1, f2.friend2
        from #friend f2
            inner join friendship f1 on f1.common_friend = f2.friend2
        where f1.level = 1
)
select p1.name AS Friend1, p2.name AS Friend2
    from friendship f
        inner join #Person p1 on f.friend1 = p1.id
        inner join #Person p2 on f.friend2 = p2.id
    where f.level = 2

您可以将其与节点/边/匹配进行比较:

CREATE TABLE dbo.Person (ID INTEGER PRIMARY KEY, name VARCHAR(50)) AS NODE;
CREATE TABLE dbo.friend (start_date DATE) AS EDGE;


INSERT INTO Person VALUES (1, 'Alice');
INSERT INTO Person VALUES (2, 'John');
INSERT INTO Person VALUES (3, 'Jacob');
INSERT INTO Person VALUES (4, 'Bob');
INSERT INTO Person VALUES (5, 'Dave');
INSERT INTO Person VALUES (6, 'Charlie');
INSERT INTO Person VALUES (7, 'Etan');
INSERT INTO Person VALUES (8, 'Fred');
INSERT INTO Person VALUES (9, 'Garry');
INSERT INTO Person VALUES (10, 'Hanna');

INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 1),(SELECT $node_id FROM dbo.Person WHERE ID = 6), '2008-05-09');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 2),(SELECT $node_id FROM dbo.Person WHERE ID = 8), '2002-07-10');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 2),(SELECT $node_id FROM dbo.Person WHERE ID = 6), '2011-11-25');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 2),(SELECT $node_id FROM dbo.Person WHERE ID = 10), '2001-11-18');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 3),(SELECT $node_id FROM dbo.Person WHERE ID = 5), '2000-02-20');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 3),(SELECT $node_id FROM dbo.Person WHERE ID = 8), '2006-10-28');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 4),(SELECT $node_id FROM dbo.Person WHERE ID = 6), '2007-06-11');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 4),(SELECT $node_id FROM dbo.Person WHERE ID = 5), '2016-05-24');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 4),(SELECT $node_id FROM dbo.Person WHERE ID = 10), '2015-11-13');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 5),(SELECT $node_id FROM dbo.Person WHERE ID = 10), '2017-02-21');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 5),(SELECT $node_id FROM dbo.Person WHERE ID = 1), '2011-02-18');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 5),(SELECT $node_id FROM dbo.Person WHERE ID = 2), '2015-08-24');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 6),(SELECT $node_id FROM dbo.Person WHERE ID = 3), '2011-06-09');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 6),(SELECT $node_id FROM dbo.Person WHERE ID = 7), '2003-09-10');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 6),(SELECT $node_id FROM dbo.Person WHERE ID = 8), '2009-07-10');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 7),(SELECT $node_id FROM dbo.Person WHERE ID = 5), '2007-02-06');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 7),(SELECT $node_id FROM dbo.Person WHERE ID = 3), '2000-02-07');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 8),(SELECT $node_id FROM dbo.Person WHERE ID = 7), '2013-04-28');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 9),(SELECT $node_id FROM dbo.Person WHERE ID = 4), '2006-08-17');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 9),(SELECT $node_id FROM dbo.Person WHERE ID = 3), '2007-01-22');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 9),(SELECT $node_id FROM dbo.Person WHERE ID = 1), '2011-06-02');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 9),(SELECT $node_id FROM dbo.Person WHERE ID = 5), '2007-08-16');
INSERT INTO dbo.friend VALUES ((SELECT $node_id FROM dbo.Person WHERE ID = 10),(SELECT $node_id FROM dbo.Person WHERE ID = 9), '2006-12-09');

-- Find 2 people who are both friends with same person
   SELECT Person1.name AS Friend1, Person2.name AS Friend2
   FROM Person Person1, friend friend1, Person Person2, 
        friend friend2, Person Person0
   WHERE MATCH(Person1-(friend1)->Person0<-(friend2)-Person2);

关于sql - 使用标准 SQL 模拟图形查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69640594/

相关文章:

sql - oracle sql 如何选择第一行和最后一行两个select

c# - 从 SQLite 数据库读取非 Unicode、非英语文本

mysql - 如何将 MS SQL Server 数据库合并到 MySQL 数据库?

sql - 如何一次执行多个存储过程?

graph - 无法通过 ID 访问特定顶点,在 Gremlin 控制台中使用 TinkerGraph

sql - 将现有列转换为标识

c# - 在不使用字符串的情况下将 ExecuteScalar 结果转换为 GUID?

sql-server - 在 SQL 中解析 JSON

php - 谷歌图和 PHP

algorithm - 最小化无间隙调度中的重叠