3

我需要执行可能导致多行的自联接,但我需要将联接限制为每条记录单行。当多行匹配连接条件时,只应使用具有最大 PK 的值行。这是一个简化的模式,假设:

CREATE TABLE #Records(
    Id int NOT NULL,
    GroupId int NOT NULL,
    Node varchar(10) NOT NULL,
    Value varchar(10) NULL,
    Meta1 varchar(10) NULL,
    Meta2 varchar(10) NULL,
    Meta3 varchar(10) NULL
)

以下是一些示例插入:

INSERT INTO #Records VALUES(1,123,'Parent', '888', 'meta1', 'meta2', 'meta3')
INSERT INTO #Records VALUES(2,123,'Guardian', '789', 'meta1', 'meta2', 'meta3')
INSERT INTO #Records VALUES(3,123,'Parent', '999', 'meta1', 'meta2', 'meta3')
INSERT INTO #Records VALUES(4,123,'Guardian', '654', 'meta1', 'meta2', 'meta3')
INSERT INTO #Records VALUES(5,123,'Sibling', '222', 'meta1', 'meta2', 'meta3')
INSERT INTO #Records VALUES(6,456,'Parent', '777', 'meta1', 'meta2', 'meta3')
INSERT INTO #Records VALUES(7,456,'Guardian', '333', 'meta1', 'meta2', 'meta3')

概括地说,我希望返回的行数等于表中的记录数。我需要 Guardian 列中的 Parent 列。对于匹配的 GroupId,Parent 应等于基于 Id 的具有“Parent”节点的最新行。我需要 Guardian 相同,但节点应该是“Guardian”。结果如下所示:

Id   GroupId    Node       Value    Meta1   Meta2   Meta3   Parent  Guardian
--- ---------- --------- --------- ------- ------- ------- ------- ----------
1     123       Parent     888      meta1   meta2   meta3    999     654     
2     123       Guardian   654      meta1   meta2   meta3    999     654
3     123       Parent     999      meta1   meta2   meta3    999     654
4     123       Guardian   789      meta1   meta2   meta3    999     654
5     123       Sibling    222      meta1   meta2   meta3    999     654
6     456       Parent     777      meta1   meta2   meta3    777     333
7     456       Guardian   333      meta1   meta2   meta3    777     333

请注意,我现在部分工作,但它不限于最新值。当所有父值节点和监护值节点都具有相同的值时,它可以正常工作。我试图限制到 MAX,但失败了。查看此查询可能会影响您的判断,因此请不要犹豫,完全抛弃它。

SELECT #Records.*, Parent,Guardian
FROM #Records
LEFT JOIN (
    SELECT MAX(Id) As ParentRow, GroupId, Value AS Parent
    FROM #Records
    WHERE Node = 'Parent'
    GROUP BY GroupId, Value
) AS Parents
ON #Records.GroupId = Parents.GroupId
LEFT JOIN (
    SELECT MAX(Id) As ParentRow, GroupId, Value AS Guardian
    FROM #Records
    WHERE Node = 'Guardian'
    GROUP BY GroupId, Value
) AS Guardians
ON #Records.GroupId = Guardians.GroupId

提前致谢!

4

2 回答 2

2

您很接近,但是您从子查询中返回了太多结果,因为您在多个字段上进行分组,因为您想要每个 GroupID 的 Max(id) 值,您可以使用该ROW_NUMBER()函数来实现这一点:

SELECT DISTINCT #Records.*, Parent,Guardian
FROM #Records
LEFT JOIN ( SELECT GroupID,Value'Parent',ROW_NUMBER() OVER(PARTITION BY GroupID ORDER BY ID DESC)'RowRank'
            FROM #Records
            WHERE Node = 'Parent'
           ) AS Parents
    ON #Records.GroupId = Parents.GroupId
      AND Parents.RowRank = 1
LEFT JOIN ( SELECT GroupID,Value'Guardian',ROW_NUMBER() OVER(PARTITION BY GroupID ORDER BY ID DESC)'RowRank'
            FROM #Records
            WHERE Node = 'Guardian'
          ) AS Guardians
    ON #Records.GroupId = Guardians.GroupId
      AND Guardians.RowRank = 1
于 2013-07-24T16:18:35.113 回答
1

以下获取原始表中每一行的组内最近的父母/监护人。它使用子句中的相关子查询select来完成工作:

select r.*,
       (select top 1 Value
        from #Records r2
        where r2.GroupId = r.GroupId and Node = 'Parent'
        order by id desc
       ) parent,
       (select top 1 Value
        from #Records r2
        where r2.GroupId = r.GroupId and Node = 'Guardian' 
        order by id desc
       ) guardian
from #Records r;

使用嵌套选择可确保原始表中的所有行都只包含一次。

在某些数据库中,您可以使用窗口/分析函数来执行此操作。例如,以下是 Oracle 语法:

select r.*,
       Last(case when Node = 'Parent' then Value end) over (partition by GroupId order by id) as Parent,
       Last(case when Node = 'Parent' then Value end) over (partition by GroupId order by id) as Guardian
from #Records;
于 2013-07-24T16:20:46.133 回答