2

我已经对 SQL Server GraphDB 进行了研究,但到目前为止我发现的所有人为示例都只使用了一个边表。例如,它总是Person-friend_of->Person。就我而言,我在我们的数据中心创建了一个已部署软件组件的图表,并且存在不同的边缘/关系。Application-connects_to->SqlDatabase和之类的东西Server-hosts->Application

我想编写一个查询,它将向我显示任意两个节点之间的最短路径,而不管使用的边缘如何。我想如果我使用 Neo4j,我会这样写MATCH

Server-*->SqlDatabase注意星号。

在 SQL Server 中是否有惯用的方法来执行此操作?

4

1 回答 1

1

从 SQL Server 2019 开始,您可以使用派生表或视图完全做到这一点。我找不到任何有关此功能的官方文档,但我在有关材料清单的视频中发现了一个小注释。

编辑:他们在那个视频中有一些链接,但我们只需要关注这个Github 示例

关键是您使用UNION ALL多个EDGE(或NODE)表作为运算符中的一个EDGE(或NODE)表MATCH

  • 如果您使用子选择,您应该使用 View,它并不总是按预期工作(见下文)
  • 您可以使用SUBSELECT,但您将无法在聚合函数中使用子选择的列(这可能是可能的,但它不容易使用,而且绝对没有记录)
  • 不能使用公用表表达式(虽然我没有特别努力,但我无法让它工作)

例子

本示例使用异构节点视图和异构边视图。它还描述了一个专业(我会称之为错误,但它可能是一个特性,这是 M$ 需要回答的问题)。如果您正在寻找两个异构节点之间的最短路径,则它们都必须是异构的。如果您从特定节点开始,而不是从异构节点开始,则无论出于何种原因,该算法都能够在距离起始节点仅一条边的地方遍历图形。

BEGIN TRANSACTION
GO

CREATE TABLE graph.SmallCities (Name varchar(1000) COLLATE Czech_100_CI_AI_SC_UTF8, SmallCity_ID INTEGER IDENTITY(666,666) PRIMARY KEY) AS NODE;
CREATE TABLE graph.LargeCities (Name varchar(1000) COLLATE Czech_100_CI_AI_SC_UTF8, LargeCity_ID INTEGER IDENTITY(666,666) PRIMARY KEY) AS NODE;
CREATE TABLE graph.Villages    (Name varchar(1000) COLLATE Czech_100_CI_AI_SC_UTF8, Village_ID   INTEGER IDENTITY(666,666) PRIMARY KEY) AS NODE;

CREATE TABLE graph.Footpaths (INDEX UQ UNIQUE nonclustered ($from_id, $to_id)) AS EDGE;
CREATE TABLE graph.Roads     (INDEX UQ UNIQUE nonclustered ($from_id, $to_id)) AS EDGE;
CREATE TABLE graph.Railways  (INDEX UQ UNIQUE nonclustered ($from_id, $to_id)) AS EDGE;

INSERT INTO graph.SmallCities (Name) VALUES (N'SmallCityOnRoad');
INSERT INTO graph.LargeCities (Name) VALUES (N'BigCityOnRailway'), (N'BiggishCityOnRailway');
INSERT INTO graph.Villages    (Name) VALUES (N'VillageInMountains');

INSERT INTO graph.Railways
  ($from_id, $to_id) 
SELECT
  L1.$node_id, L2.$node_id
FROM
  graph.LargeCities AS L1,
  graph.LargeCities AS L2
WHERE
  L1.Name = N'BigCityOnRailway' 
  AND L2.Name = N'BiggishCityOnRailway';

INSERT INTO graph.Roads
  ($from_id, $to_id) 
SELECT
  L1.$node_id, L2.$node_id
FROM
  graph.LargeCities AS L1,
  graph.SmallCities AS L2
WHERE
  L1.Name = N'BiggishCityOnRailway'
  AND L2.Name = N'SmallCityOnRoad';

INSERT INTO graph.Footpaths
  ($from_id, $to_id) 
SELECT
  L1.$node_id, L2.$node_id
FROM
  graph.SmallCities AS L1,
  graph.Villages AS L2
WHERE
  L1.Name = N'SmallCityOnRoad'
  AND L2.Name = N'VillageInMountains';

GO

CREATE VIEW graph.AllResidentialAreas AS
  SELECT
    LC.$node_id AS node_id,
    LC.Name,
    LC.LargeCity_ID AS Area_ID,
    'Large city' AS AreaType
  FROM
    graph.LargeCities AS LC
  UNION ALL
  SELECT
    SC.$node_id AS node_id,
    SC.Name,
    SC.SmallCity_ID,
    'Small city' AS AreaType
  FROM
    graph.SmallCities AS SC
  UNION ALL
  SELECT
    V.$node_id AS node_id,
    V.Name,
    V.Village_ID,
    'Village' AS AreaType
  FROM
    graph.Villages AS V;

GO

CREATE VIEW graph.AllPaths AS
  SELECT
    $edge_id AS edge_id,
    'Railway' AS PathType
  FROM
    graph.RailWays
  UNION ALL
  SELECT
    $edge_id,
    'Road' AS PathType
  FROM
    graph.Roads
  UNION ALL
  SELECT
    $edge_id,
    'Footpath' AS PathType
  FROM
    graph.Footpaths;

GO

/*************
ERROR
*/
SELECT
  STRT.Name AS FromArea,
  LAST_VALUE(NOD.Name) within GROUP (graph path) AS ToArea,
  STRING_AGG(PTH.PathType, '->') within GROUP (graph path) AS Way
FROM
  --graph.AllResidentialAreas          AS STRT,
  graph.LargeCities                  AS STRT, -------this is a problem, view vs edge table
  graph.AllPaths            FOR PATH AS PTH,
  graph.AllResidentialAreas FOR PATH AS NOD
WHERE 1=1
  AND MATCH(
    --STRT-(PTH)->NOD
    SHORTEST_PATH(
      STRT(-(PTH)->NOD)+
    )
  )
  AND STRT.Name = 'BigCityOnRailway'

/*
output:
FromArea         ToArea               Way
BigCityOnRailway BiggishCityOnRailway Railway
BigCityOnRailway SmallCityOnRoad      Railway->Road
*/

/*****************
WORKS
*/
SELECT
  STRT.Name AS FromArea,
  LAST_VALUE(NOD.Name) within GROUP (graph path) AS ToArea,
  STRING_AGG(PTH.PathType, '->') within GROUP (graph path) AS Way
FROM
  graph.AllResidentialAreas          AS STRT,
  --graph.LargeCities                  AS STRT,
  graph.AllPaths            FOR PATH AS PTH,
  graph.AllResidentialAreas FOR PATH AS NOD
WHERE 1=1
  AND MATCH(
    --STRT-(PTH)->NOD
    SHORTEST_PATH(
      STRT(-(PTH)->NOD)+
    )
  )
  AND STRT.Name = 'BigCityOnRailway'
  AND STRT.AreaType = 'Large city';

/*
FromArea         ToArea               Way
BigCityOnRailway BiggishCityOnRailway Railway
BigCityOnRailway SmallCityOnRoad      Railway->Road
BigCityOnRailway VillageInMountains   Railway->Road->Footpath
*/

GO
IF @@TRANCOUNT > 0
  ROLLBACK TRANSACTION
GO
于 2021-11-21T14:45:28.840 回答