寻找缺失的逆关系

dav*_*vid 6 sql-server t-sql

我正在为一个我觉得应该很简单的查询而苦苦挣扎,但我无法理解它。

我们有一个单独的表来保存不同方之间的关系。该表应该为每个关系包含一行。然而,我们注意到一些关系缺失了,我们想重建它们。基本上每个关系应该有两行,关系和逆。

所以,一张健康的桌子应该是这样的……

Id | PartyId | OtherPartyId | RelType | RelStatus
1   1111      2211            1         1
2   2211      1111            1         1
3   3344      4444            1         2
4   4444      3344            1         2
Run Code Online (Sandbox Code Playgroud)

但是,我们有这样的情况......

Id | PartyId | OtherPartyId | RelType | RelStatus
1   1111      2211            1         1
2   2211      1111            1         1
3   3344      4444            1         2
4   5555      2224            1         2
5   2224      5555            1         2
Run Code Online (Sandbox Code Playgroud)

方 3344 和 4444 之间的逆关系缺失,所以我只想返回该行,以便我可以插入逆关系。

'id' 列是无关紧要的,因为它只是一个标识列,但 RelType 和 RelStatus 很重要,因为它们对于反向关系需要相同 - 例如,在 4444 和 3344 之间可能存在确实存在的关系,但它是不同的类型和状态,所以我们应该忽略它(当然除非它也缺少逆元!)。

我尝试创建一个复合密钥系统来尝试识别丢失的 rels,但我发现它在某些情况下会带回重复项:

            SELECT
        CASE WHEN [PartyId] < [OtherPartyId] 
            THEN CAST([OtherPartyId] AS NVARCHAR(50)) + '.' + CAST([PartyId] AS NVARCHAR(50)) + '.' + CAST([RelType] AS NVARCHAR(50)) +  '.' + CAST([RelStatus] AS NVARCHAR(50))  
            ELSE CAST([PartyId] AS NVARCHAR(50)) +  '.'  + CAST([OtherPartyId] AS NVARCHAR(50)) +  '.' + CAST([RelType] AS NVARCHAR(50)) +  '.' + CAST([RelStatus] AS NVARCHAR(50)) 
        END AS CompKey
        INTO #partyinvs
        FROM PartyRelationships;

        SELECT  CompKey
        FROM    #partyinvs
        GROUP BY CompKey
        HAVING  COUNT(*) < 2
Run Code Online (Sandbox Code Playgroud)

我希望这是有道理的?

谢谢,大卫

Ran*_*gen 6

使用EXCEPT可以帮助您编写此查询。

简而言之,如果第一个 select 的任何行与 inverse + 正确RelType &不匹配RelStatus ,则返回它们。

SELECT PartyId , OtherPartyId, RelType , RelStatus   
FROM dbo.Table
EXCEPT 
SELECT OtherPartyId , PartyId, RelType , RelStatus   
FROM dbo.Table;
Run Code Online (Sandbox Code Playgroud)

数据库<>小提琴

请记住,如果存在重复的行(具有反向关系的 EG 3 行),则不会返回这些行。

例子


数据线

CREATE TABLE dbo.bla(ID INT IDENTITY(1,1) PRIMARY KEY NOT NULL,
                     PartyId int,
                     OtherPartyId int,
                     RelType int,
                     RelStatus int);
Run Code Online (Sandbox Code Playgroud)

数据管理语言

INSERT INTO dbo.Bla(PartyId , OtherPartyId , RelType , RelStatus)
VALUES
(1111,2211           ,1       ,1),
(2211,1111           ,1       ,1),
(3344,4444           ,1       ,2),
(5555,2224           ,1       ,2),
(4444,3344           ,2      ,2),
(1111,2211           ,2      ,2);-- different type or status
Run Code Online (Sandbox Code Playgroud)

询问

SELECT PartyId , OtherPartyId, RelType , RelStatus   
FROM dbo.Bla
EXCEPT 
SELECT OtherPartyId , PartyId, RelType , RelStatus   
FROM dbo.Bla;
Run Code Online (Sandbox Code Playgroud)

结果

PartyId OtherPartyId    RelType RelStatus
1111    2211            2         2
3344    4444            1         2
4444    3344            2         2
5555    2224            1         2
Run Code Online (Sandbox Code Playgroud)

另一种解决方案可能是使用NOT EXISTS, EG 当您还需要该Id字段时。

SELECT Id,PartyId , OtherPartyId, RelType , RelStatus   
FROM dbo.Bla b
WHERE NOT EXISTS
(
SELECT * 
FROM dbo.Bla b2
where b.OtherPartyId = b2.PartyId
AND   b.PartyId = b2.OtherPartyId
AND b.RelType = b2.RelType
AND b.RelStatus = b2.RelStatus
);
Run Code Online (Sandbox Code Playgroud)

数据库<>小提琴


Lau*_*gil 5

执行此操作的最常见查询版本类似于:

INSERT INTO PartyRelationships (
    PartyId, 
    OtherPartyId, 
    RelType, 
    RelStatus
    )
SELECT pr.OtherPartyID, 
    pr.PartyId,
    pr.RelType,
    pr.RelStatus
FROM papr
LEFT JOIN PartyRelationships pri
    ON pr.PartyId = pri.OtherPartyId
    AND pr.otherPartyId = pri.PartyId
    AND pr.RelType = pri.RelType
    AND pr.RelStatus = pri.RelStatus
WHERE pri.partyId is null
Run Code Online (Sandbox Code Playgroud)

@randi-vertongen 的 EXCEPT 示例也是执行此操作的一种方法,并且可能更有效。另一种不是上述示例的方法是 MERGE 方法

MERGE INTO PartyRelationships AS pr
USING PartyRelationships AS pri
    ON pr.PartyID = pri.OtherPartyId
    AND pr.OtherPartyId = pri.PartyId
    AND pr.RelType = pri.RelType
    AND pr.RelStatus = RelStatus
WHEN NOT MATCHED THEN
    INSERT(PartyId, OtherPartyId, RelType, RelStatus)
    VALUES(pri.OtherPartyId, pri.PartyId, pri.RelType, pri.RelStatus);
Run Code Online (Sandbox Code Playgroud)

这与第一个示例基本相同,只是使用了 MERGE 语法。然而,这个操作是一种自我合并,所以这对我来说是合乎逻辑的。

您选择的任何选项都适用于您的要求。