关于sql server:基于另一列的最大值的列上的SQL内连接

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

本问题已经有最佳答案,请猛点这里访问。

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

1
2
3
4
5
6
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)

1
2
3
4
ID    USER    Name
425   John    Skyline
426   John    Violin
427   Joe     Pura

脚本

1
2
3
4
5
6
7
ID    ScenarioID    STATUS
425   1             active
425   2             active
425   3             done
426   1             active
426   2             active
427   1             done

期望的输出

1
2
3
4
ID    USER    Name    STATUS
425   John    Skyline done
426   John    Violin  active
427   Joe     Pura    done


您可以使用CROSS APPLY查找每个值的最新值:

1
2
3
4
5
6
7
8
9
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

1
2
3
4
5
6
7
8
9
10
;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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
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;


如果您有SQL Server 2008或更高版本,则可以使用ROW_NUMBER()函数来实现所需。 它将避免两次查询同一个表或执行连接。

1
2
3
4
5
6
7
8
9
10
11
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