SQL Server:具有软删除功能的 Graph-DB 未按预期工作

Posted

技术标签:

【中文标题】SQL Server:具有软删除功能的 Graph-DB 未按预期工作【英文标题】:SQL Server : Graph-DB with soft delete isn't working as expected 【发布时间】:2021-05-07 14:06:51 【问题描述】:

我尝试在 SQL Server 中使用图形功能。现在我遇到了软删除问题。

我有以下图表

[1] -> [2] -> [9 (deleted)] -> [4]

当我运行以下脚本时

CREATE TABLE MyNode 
(
    [Id] [bigint] NOT NULL,
    [IsDeleted] [bit] NOT NULL,
) AS NODE;

CREATE TABLE MyEdge 
(
    State [int] NOT NULL
) AS EDGE;

INSERT INTO MyNode (Id, IsDeleted)
VALUES (1, 0), (2, 0), (4, 0), (9, 1);

INSERT INTO MyEdge
VALUES
( (SELECT $node_id FROM MyNode WHERE Id = 1), (SELECT $node_id FROM MyNode WHERE Id = 2), 1),
( (SELECT $node_id FROM MyNode WHERE Id = 2), (SELECT $node_id FROM MyNode WHERE Id = 9), 1),
( (SELECT $node_id FROM MyNode WHERE Id = 9), (SELECT $node_id FROM MyNode WHERE Id = 4), 1)
;

SELECT
    src.Id ID_SOURCE
,   LAST_VALUE(trgt.Id) WITHIN GROUP (GRAPH PATH) AS ID_TARGET
,   STRING_AGG(trgt.Id, '->') WITHIN GROUP (GRAPH PATH) AS ID_CHAIN
--, STRING_AGG(compare.State, '->') WITHIN GROUP (GRAPH PATH) AS STATE_CHAIN
--, STRING_AGG(trgt.IsDeleted, '->') WITHIN GROUP (GRAPH PATH) AS DELETED_CHAIN
FROM
    MyNode AS src
,   (   SELECT
            *
        FROM
            MyEdge
        WHERE
            State = 1
    ) FOR PATH AS compare
,   (   SELECT
            *
        FROM
            MyNode
        WHERE
            IsDeleted = 0
    ) FOR PATH AS trgt
WHERE
    MATCH ( SHORTEST_PATH( src(-(compare)->trgt)+ ) )
    AND src.Id = 1;

SELECT
    src.Id AS SOURCE_ID
,   ed.State AS EDGE_STATE
,   trgt.Id AS TARGET_ID
FROM
    MyNode AS src
,   MyEdge AS ed
,   MyNode AS trgt
WHERE
    MATCH( src-(ed)->trgt )
    AND src.Id = 2;


DROP TABLE MyNode;
DROP TABLE MyEdge;

(提示:这仅适用于 SQL-Server 2019)

我得到以下结果

ID_SOURCE ID_TARGET ID_CHAIN
1 2 2
1 4 2->4

没有边缘2->4,而是2->9->4。但是节点 9 被删除了,所以它被用于图的遍历,但在输出中被抑制了。

这是 SQL-Server 中的错误还是我做错了什么?

或者还有其他方法我应该使用 Graph-DB 的软删除吗?

【问题讨论】:

交叉投递到feedback.azure.com/forums/908035-sql-server/suggestions/… 【参考方案1】:

您可以过滤掉来自/到软删除节点的边缘

....
(   
SELECT e.*
FROM MyEdge as e
WHERE
    e.State = 1
    and exists(select * from MyNode as x where x.IsDeleted = 0 and x.$node_id = e.$to_id)
    --and exists(select * from MyNode as x where x.IsDeleted = 0 and x.$node_id = e.$from_id)
) FOR PATH AS compare
....

以下查询不知何故返回错误(执行几秒钟后):

SELECT
    src.Id ID_SOURCE
,   LAST_VALUE(trgt.Id) WITHIN GROUP (GRAPH PATH) AS ID_TARGET
,   STRING_AGG(trgt.Id, '->') WITHIN GROUP (GRAPH PATH) AS ID_CHAIN
--, STRING_AGG(compare.State, '->') WITHIN GROUP (GRAPH PATH) AS STATE_CHAIN
--, STRING_AGG(trgt.IsDeleted, '->') WITHIN GROUP (GRAPH PATH) AS DELETED_CHAIN
FROM
    MyNode AS src
,   
(   
SELECT e.*
FROM MyEdge as e
WHERE
    e.State = 1
    and exists(select * from MyNode as x where x.IsDeleted = 0 and x.$node_id = e.$to_id)
    --and exists(select * from MyNode as x where x.IsDeleted = 0 and x.$node_id = e.$from_id)
) FOR PATH AS compare

,   (   SELECT
            *
        FROM
            MyNode
        WHERE
            IsDeleted = 0
    ) FOR PATH AS trgt
WHERE
    MATCH ( SHORTEST_PATH( src(-(compare)->trgt)+ ) )

...并且通过向 src 表附加条件/引用来消除错误,这始终是正确的:

AND src.Id = src.Id --or src.IsDeleted = src.IsDeleted

肯定有问题 [Microsoft SQL Server 2019 (RTM-CU8-GDR) (KB4583459)]..

【讨论】:

谢谢,我没有考虑过滤边缘。如果这个解决方案足够快,我会尝试。如果没有,我将在边缘添加一个 IsDeleted-Flag 并在删除和取消删除时更新它们。【参考方案2】:

将 isDeleted 标志合并到边缘表中,并在相应的(到/从)节点被标记为软删除时更新该标志,并在边缘表子查询上使用 isDeleted 是首选方式。我们不建议直接比较 $node_id 和 $from_id,而是通过 MATCH 函数隐式比较。

【讨论】:

以上是关于SQL Server:具有软删除功能的 Graph-DB 未按预期工作的主要内容,如果未能解决你的问题,请参考以下文章

如何删除具有数据库所有者权限的 SQL Server 用户

需要从不包含隐藏 graph_id 列的 SQL Server Graph Table 创建视图

SQL Server-一次删除具有IF EXISTS的多列

SQL Server Graph:如何识别子图?

如何在具有相同 ID 的列中选择不同的值然后删除它们 PHP SQL Server

SQL Server Graph Tables - 浏览某些节点以找到结束