SQL Server 图形数据库 - 使用多种边类型的最短路径

2024-04-17

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

我想编写一个查询,该查询将显示任意两个节点之间的最短路径,无论使用什么边。我想如果我使用 Neo4j,我会写MATCH就像这样:

Server-*->SqlDatabase注意星号。

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


从 SQL Server 2019 开始,您可以使用派生表或视图来完成此操作。我找不到此功能的任何官方文档,但我在有关物料清单的视频 https://learn.microsoft.com/shows/Data-Exposed/Exploding-Bill-of-Materials-using-Graph-Shortest-Path。 问题是,它目前(SQL Server 2019)存在缺陷,并且无法按预期工作(或如我期望的那样工作)。

编辑:他们在该视频中有一些链接,但我们只需要关心这个GitHub 示例 https://github.com/microsoft/sql-server-samples/tree/master/samples/features/sql-graph/DerivedTablesAndViewsInGraphMatch?WT.mc_id=dataexposed-c9-niner.

编辑 2:我发现了一个主要错误,它基本上使异构查询无法在实际使用中使用。

关键是你使用UNION ALL多个的EDGE (or NODE) 表作为一个表EDGE (or NODE)表中的MATCH操作员。

  • You 应该使用视图如果您使用子选择,您可以做的事情会受到一些限制(见下文)
  • 您可以使用子选择,但是您将无法在聚合函数中使用子选择的列(可能是可能的,但它不容易使用,而且绝对没有记录)
  • You can not use 公用表表达式

Example

本示例使用异质的节点视图和异质的边缘视图。它还描述了两个主要的(我会称之为bug,但它可能是一个特性,这是M$要回答的问题)bugs:

  1. 如果您正在寻找两个之间的最短路径异质的节点,所有的节点都必须是异质的。如果你从一个开始specific节点然后继续异质的节点,无论出于何种原因,算法都能够遍历距离起始节点仅一条边的图。
  2. 如果您尝试在node or edge路径中的表,它们目前仅适用于edge表行;上的聚合函数noderows 返回随机(?)垃圾。
/** TOC:
* 1. prepare tables
* 2. prepare data
* 3. prepare heterogenous views
* 4. QUERIES
* 4.a. BUG 1
* 4.b. BUG 2
* 4.c. WORKS
**/

BEGIN TRANSACTION
GO

/*******************\
| 1. prepare tables |
\*******************/
CREATE TABLE graph.SmallCities   (Name nvarchar(1000), Weight INTEGER, SmallCity_ID INTEGER IDENTITY(666,666) PRIMARY KEY) AS NODE;
CREATE TABLE graph.LargeCities   (Name nvarchar(1000), Weight INTEGER, LargeCity_ID INTEGER IDENTITY(666,666) PRIMARY KEY) AS NODE;
CREATE TABLE graph.Villages      (Name nvarchar(1000), Weight INTEGER, Village_ID   INTEGER IDENTITY(666,666) PRIMARY KEY) AS NODE;
CREATE TABLE graph.Hamlets       (Name nvarchar(1000), Weight INTEGER, Hamlet_ID    INTEGER IDENTITY(666,666) PRIMARY KEY) AS NODE;

CREATE TABLE graph.Hikes     (INDEX UQ UNIQUE nonclustered ($from_id, $to_id)) AS EDGE;
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, Weight) VALUES (N'SmallCityOnRoad', 3);
INSERT INTO graph.LargeCities (Name, Weight) VALUES (N'BigCityOnRailway', 5), (N'BiggishCityOnR&R', 4);
INSERT INTO graph.Villages    (Name, Weight) VALUES (N'VillageInMountains', 2);
INSERT INTO graph.Hamlets     (Name, Weight) VALUES (N'HutInThePass', 1);

/*****************\
| 2. prepare data |
\*****************/
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'BiggishCityOnR&R';

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'BiggishCityOnR&R'
  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';

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

GO

/*******************************\
| 3. prepare heterogenous views |
\*******************************/
CREATE VIEW graph.AllResidentialAreas AS
  SELECT
    LC.$node_id AS node_id,
    LC.Name,
    LC.Weight,
    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.Weight,
    SC.SmallCity_ID,
    'Small city' AS AreaType
  FROM
    graph.SmallCities AS SC
  UNION ALL
  SELECT
    V.$node_id AS node_id,
    V.Name,
    V.Weight,
    V.Village_ID,
    'Village' AS AreaType
  FROM
    graph.Villages AS V
  UNION ALL
  SELECT
    H.$node_id AS node_id,
    H.Name,
    H.Weight,
    H.Hamlet_ID,
    'Hamlet' AS AreaType
  FROM
    graph.Hamlets AS H;

GO

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

GO

/************\
| 4. QUERIES |
\************/

/*************
| 4.a. BUG 1 - combining views and underlaying tables doesn't work
*/
SELECT
  STRT.Name AS FromArea,
  LAST_VALUE(NOD.Name) within GROUP (graph PATH) AS ToArea,
  STRT.NAME + '->' + STRING_AGG(NOD.Name, '->') WITHIN GROUP (graph PATH) AS Way
FROM
  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(
    SHORTEST_PATH(
      STRT(-(PTH)->NOD)+
    )
  )
  AND STRT.NAME = 'BigCityOnRailway';

/**OUTPUT:
--The problem is, that the SHORTEST_PATH doesn't "see" more than one step behind the starting underlaying table
FromArea          ToArea            Way
BigCityOnRailway  BiggishCityOnR&R  BigCityOnRailway->BiggishCityOnR&R
BigCityOnRailway  SmallCityOnRoad   BigCityOnRailway->BiggishCityOnR&R->SmallCityOnRoad
*/

/*****************
| 4.b. BUG 2 - using node rows along the SHORTEST_PATH found
*/
SELECT
  STRT.Name AS FromArea,
  LAST_VALUE(NOD.Name) within GROUP (graph PATH) AS ToArea,
  STRING_AGG(PTH.PathType, '->') WITHIN GROUP (graph PATH) AS Path,
  STRT.NAME + '->' + STRING_AGG(NOD.Name, '->') WITHIN GROUP (graph PATH) AS Way, --this has problem
  SUM(NOD.Weight) WITHIN GROUP (graph PATH) AS Weight, --this has similar problem
  COUNT(PTH.PathType) WITHIN GROUP (graph PATH) AS Path_Length
FROM
  graph.AllResidentialAreas          AS STRT,
  graph.AllPaths            FOR PATH AS PTH,
  graph.AllResidentialAreas FOR PATH AS NOD
WHERE 1=1
  AND MATCH(
    SHORTEST_PATH(
      STRT(-(PTH)->NOD)+
    )
  )
  AND STRT.Name = 'BigCityOnRailway'
  AND STRT.AreaType = 'Large city';

/**OUTPUT
--This correctly finds the "transitive closure" (columns FromArea and ToArea)
--This correctly finds the edges that need to be traversed (column Path)
--BUT the nodes along the way are wrong - see the last two rows:
--  first, second and the last nodes are OK,
--  but all the nodes between are just the first node repeated
--  this is also visible in the Weight column, where the correct weights should be (4, 7, 9, 10)
FromArea          ToArea              Path                           Way                                                                                   Weight  Path_Length
BigCityOnRailway  BiggishCityOnR&R    Railway                        BigCityOnRailway->BiggishCityOnR&R                                                    4       1
BigCityOnRailway  SmallCityOnRoad     Railway->Road                  BigCityOnRailway->BiggishCityOnR&R->SmallCityOnRoad                                   7       2
BigCityOnRailway  VillageInMountains  Railway->Road->Footpath        BigCityOnRailway->BiggishCityOnR&R->BigCityOnRailway->VillageInMountains              11      3
BigCityOnRailway  HutInThePass        Railway->Road->Footpath->Hike  BigCityOnRailway->BiggishCityOnR&R->BigCityOnRailway->BigCityOnRailway->HutInThePass  15      4
*/

/***********************************
| 4.c. WORKS, but isn't heterogenous
*/
CREATE TABLE graph.AllAreas (Name nvarchar(1000), Weight INTEGER, Area_ID INTEGER IDENTITY(666,666) PRIMARY KEY, AreaType VARCHAR(1000)) AS NODE;
CREATE TABLE graph.AllWays (PathType VARCHAR(1000), INDEX UQ UNIQUE nonclustered ($from_id, $to_id)) AS EDGE;

INSERT INTO graph.AllAreas
  (Name, Weight, AreaType)
SELECT
  ARA.Name,
  ARA.Weight,
  ARA.AreaType
FROM
  graph.AllResidentialAreas AS ARA WITH(NOLOCK);

INSERT INTO graph.AllWays
  ($from_id, $to_id, PathType)
SELECT
  AA_FROM.$node_id,
  AA_TO.$node_id,
  AP.PathType
FROM
  graph.AllPaths AS AP
  JOIN graph.AllResidentialAreas AS ARA_FROM ON ARA_FROM.node_id = AP.from_id
  JOIN graph.AllResidentialAreas AS ARA_TO   ON ARA_TO.node_id   = AP.to_id
  JOIN graph.AllAreas AS AA_FROM ON AA_FROM.Name = ARA_FROM.Name
  JOIN graph.AllAreas AS AA_TO   ON AA_TO.Name   = ARA_TO.Name;

SELECT
  STRT.Name AS FromArea,
  LAST_VALUE(NOD.Name) within GROUP (graph PATH) AS ToArea,
  STRING_AGG(PTH.PathType, '->') WITHIN GROUP (graph PATH) AS Path,
  STRT.NAME + '->' + STRING_AGG(NOD.Name, '->') WITHIN GROUP (graph PATH) AS Way, --this has problems too, 
  SUM(NOD.Weight) WITHIN GROUP (graph PATH) AS Weight, --this has similar problem
  COUNT(PTH.PathType) WITHIN GROUP (graph PATH) AS Path_Length
FROM
  graph.AllAreas          AS STRT,
  graph.AllWays  FOR PATH AS PTH,
  graph.AllAreas FOR PATH AS NOD
WHERE 1=1
  AND MATCH(
    SHORTEST_PATH(
      STRT(-(PTH)->NOD)+
    )
  )
  AND STRT.Name = 'BigCityOnRailway'
  AND STRT.AreaType = 'Large city';

/**OUTPUT:
FromArea          ToArea              Path                           Way                                                                                    Weight  Path_Length
BigCityOnRailway  BiggishCityOnR&R    Railway                        BigCityOnRailway->BiggishCityOnR&R                                                     4       1
BigCityOnRailway  SmallCityOnRoad     Railway->Road                  BigCityOnRailway->BiggishCityOnR&R->SmallCityOnRoad                                    7       2
BigCityOnRailway  VillageInMountains  Railway->Road->Footpath        BigCityOnRailway->BiggishCityOnR&R->SmallCityOnRoad->VillageInMountains                9       3
BigCityOnRailway  HutInThePass        Railway->Road->Footpath->Hike  BigCityOnRailway->BiggishCityOnR&R->SmallCityOnRoad->VillageInMountains->HutInThePass  10      4
*/

GO
IF @@TRANCOUNT > 0
  ROLLBACK TRANSACTION
GO
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

SQL Server 图形数据库 - 使用多种边类型的最短路径 的相关文章

  • 当没有文件可供下载时,如何避免 SSIS FTP 任务失败?

    我正在使用 SQL Server 2005 并在 SSIS 中创建 ftp 任务 有时会有文件需要通过 ftp 传输 有时则不会 如果没有文件 我不希望任务或包失败 我已将从 ftp 任务到下一个任务的箭头更改为 完成 以便包运行 我已将允
  • 获取下一个ID而不插入行

    在 SQL SQL Server 中是否可以在插入行之前从表中的标识列检索下一个 ID 整数 而无需实际插入行 如果删除了最近的行 则这不一定是最高 ID 加 1 我问这个问题是因为我们偶尔需要用新行更新实时数据库 行的 ID 在我们的代码
  • T-SQL 按最旧日期和唯一类别选择行

    我正在使用 Microsoft SQL 我有一个表 其中包含按两个不同类别存储的信息和一个日期 例如 ID Cat1 Cat2 Date Time Data 1 1 A 11 00 456 2 1 B 11 01 789 3 1 A 11
  • SQL Server 到 er 模型

    是否有程序可以将 SQL Server 数据库图表转换为 er 模型 或者从 SQL Server 服务器创建数据库的 er 模型 在 SQL Server 中 Management Studio 中的每个数据库都有 数据库图 功能 您可以
  • 无法更新 .mdf 数据库,因为该数据库是只读的(Windows 应用程序)

    我使用 C 创建了一个数据库 Windows 应用程序 我的应用程序在 Windows XP 上成功运行 但在 Vista 或 Windows 7 系统上无法正确执行 我的应用程序显示类似以下内容的消息 无法更新 mdf 数据库 因为该数据
  • 有很多数据库视图可以吗?

    我很少 每月 每季度 使用 Microsoft SQL Server 2005 数据库视图生成数百份 Crystal Reports 报告 在我不读取这些视图的所有时间里 这些视图是否会浪费 CPU 周期和 RAM 因为我很少从视图中读取数
  • 避免数据集中出现重复名称

    我正在从表中获取数据并绑定到标签并在 gridview 中下拉 但我想从表中过滤重复的名称并将相应的日期分配给 DDL 如何做到这一点 或者还有其他选择吗 private DataSet get string sql select Id N
  • 如何从 Neo4j 服务器插件登录?

    我正在尝试调试我正在编写的 Neo4J 服务器插件中的问题 有可以输出的日志吗 在哪里或如何执行此操作并不明显 好问题 我想你可以使用 Java 日志记录吗 这应该被路由到正常的日志系统中
  • 无论如何要解密加密的sql server存储过程吗?

    我有几个 ms sql server 2000 存储过程 很久以前就被前雇员加密了 一切都很好 直到我们需要稍微改变一下 有什么方法可以检索源代码吗 或者重写是唯一的选择 多谢 或者免费 谷歌是你的朋友 http searchsqlserv
  • 从每个子集中选择最大值

    我在这里敲头 我觉得自己很愚蠢 因为我确信我以前做过类似的事情 但我一辈子都不记得是怎么做的 我想那一天 gt 假设我有以下数据 gt 和一个返回此数据的查询 gt 但我想要这个 ID FirstID ID FirstID ID First
  • 如何在 SQL Server 中创建文件格式

    我正在尝试在 SQL Server 2017 中试验外部文件 但在第一步中遇到了困难 数据是管道分隔的 我试图遵循文档中的语法 这需要一个FILE FORMAT 以下是 Microsoft 的语法 CREATE EXTERNAL TABLE
  • 如何在 SqlDataReader.Read() 期间从死锁异常中恢复

    我的 NET 应用程序的事件日志显示 它在从 Sql Server 读取数据时偶尔会出现死锁 这种情况通常非常罕见 因为我们已经优化了查询以避免死锁 但有时仍然会发生 过去 我们在调用ExecuteReader函数在我们的SqlComman
  • 在 Sql STUFF 命令中将最后一项的逗号分隔符替换为“and”

    如果我查询的输出是英国 美国 印度 是否可以像英国 美国那样显示查询结果and India 这是我的查询 Select stuff Select Distinct Country as text from tbl Country where
  • SQL Server 中的嵌套事务

    sql server 允许嵌套事务吗 如果是的话那么交易的优先级是什么 来自 SQL Server 上的 MSDN 文档 嵌套交易 http msdn microsoft com en us library ms189336 SQL 90
  • 如何搜索表中的所有列?

    如何在 SQL Server 中搜索表的所有列 SELECT FROM yourtable WHERE val IN field1 field2 field3 field4 如果您正在寻找精确的全场比赛 如果你正在寻找子字符串匹配 你将不得
  • 将图像列保存到 SQL Server 2000 中的文件

    我在 SQL Server 2000 中有一个包含图像列的表 我需要将图像数据保存到文件系统上的文件中 在 SQL Server 2005 中 我可以使用 ADODB Stream 对象进行文件 I O 但这在 SQL Server 200
  • SQL Server 大小写/排序规则问题

    今天我在客户数据库中遇到了一个奇怪的情况 SQL Server 2005 数据库排序规则不区分大小写 因此我可以使用任何大小写编写 SQL 查询 没有任何问题 除了一个 一张特定表中的一个特定列称为 DeadZone 如果我这样查询 从表名
  • SQL 查询将文本数据存储在 Varbinary(max) 中

    有没有办法让 varbinary 在 SQL Server 中接受文本数据 这是我的情况 我有相当大量的 XML 我计划以 压缩 格式存储它们 这意味着 Varbinary 但是 当我进行调试时 我希望能够翻转配置开关并以纯文本形式存储 以
  • 我可以根据多列删除数据库重复项吗?

    I 不久前问过这个问题 https stackoverflow com questions 4952250 how to delete duplicates from a database table based on a certain
  • SQL Server 抱怨无效的 json

    我正在使用 Azure 数据工厂和 Azure SQL 数据库编写 ETL 工具 数据工厂捕获映射数据流的输出并将其作为字符串插入到 SQL Server 表 Audit OperationsEventLog 的 StatusMessage

随机推荐