SQL 基于另一列的最大值对一列进行内部联接

SQL inner join on a column based on max value from another column

我有两个 table,一个 "master" 是名称的主列表,第二个 "scenario" 是主列表中每个名称的多个场景的列表。我希望我的 INNER JOIN 查询从 "scenario" table 中获取具有列状态的 ID 主列表,但仅获取基于 scenarioID 的最新状态。这是我尝试过的代码,tables 具有所需的输出

SELECT DISTINCT a.[user], a.ID, a.Name, b.status
from master a
INNER JOIN scenario b ON a.ID = b.ID
WHERE
    b.scenarioID = (
           SELECT max(scenarioID) FROM scenario c2 WHERE c2.ID=c.ID)

硕士

ID    user    Name
425   John    Skyline
426   John    Violin
427   Joe     Pura

场景

ID    ScenarioID    status
425   1             active
425   2             active
425   3             done
426   1             active
426   2             active
427   1             done

期望输出

ID    user    Name    status
425   John    Skyline done
426   John    Violin  active
427   Joe     Pura    done

您可以通过 CROSS APPLY 查找每个值的最新值来执行此操作:

Select  M.ID, M.[User], M.Name, X.Status
From    [Master]    M
Cross Apply
(
    Select  Top 1 S.Status
    From    Scenario    S
    Where   S.ID = M.ID
    Order By S.ScenarioID Desc
) X

另一种方法是在 ID 上使用 ROW_NUMBER() PARTITIONED,在 ScenarioID DESC 上使用 ORDERED

;With OrderedStatuses As
(
    Select  M.Id, M.[User], M.Name, S.Status,
            Row_Number() Over (Partition By S.Id Order By S.ScenarioID Desc) RN
    From    [Master]    M
    Join    Scenario    S   On  S.Id = M.Id
)
Select  Id, [User], Name, Status
From    OrderedStatuses
Where   RN = 1

如果您有 SQL Server 2008 或更高版本,您可以使用 ROW_NUMBER() 函数来实现您想要的。它将避免查询相同的 table 两次或执行连接。

SELECT *
FROM    (

            SELECT   a.[user]
                    ,a.ID
                    ,a.Name
                    ,b.status 
                    ,ROW_NUMBER() OVER (PARTITION BY a.ID ORDER BY b.scenarioID DESC) AS VersionRank
            from [master] a INNER JOIN scenario b ON a.ID = b.ID 
        ) Result
WHERE   Result.VersionRank = 1

这里有一个使用 CTE 的稍微不同的公式,我通常发现它比子查询更容易阅读(当然,你的里程可能会有所不同)。

declare @Master table
(
    ID bigint,
    [user] varchar(16),
    Name varchar(16)
);

declare @Scenario table
(
    ID bigint,
    ScenarioID bigint,
    [status] varchar(16)
);

insert @Master values
    (425, 'John', 'Skyline'),
    (426, 'John', 'Violin'),
    (427, 'Joe', 'Pura');
insert @Scenario values
    (425, 1, 'active'),
    (425, 2, 'active'),
    (425, 3, 'done'),
    (426, 1, 'active'),
    (426, 2, 'active'),
    (427, 1, 'done');

with ReversedScenarioCTE as
(
    select
        ID,
        [status],
        rowNumber = row_number() over (partition by ID order by ScenarioID desc)
    from
        @Scenario
)
select
    M.ID,
    M.[user],
    M.Name,
    S.[status]
from
    @Master M
    inner join ReversedScenarioCTE S on
        M.ID = S.ID and
        S.rowNumber = 1;