一起选择 MIN Date(使用 WINDOW 函数)和 MAX Date(使用 JOIN 关联字段)

AS9*_*S91 4 sql sql-server greatest-n-per-group window-functions sql-server-2012

表 T1

+----+------------+------------+--------+------+
| ID |   Sdate    |   Edate    | Reason | Type |
+----+------------+------------+--------+------+
|  1 | 5/30/2016  |            |        | A    |
|  1 | 1/19/2016  | 12/15/2016 | USD    | B    |
|  1 | 11/20/2016 | 10/1/2016  | IT     | B    |
|  2 | 10/25/2016 |            |        | A    |
|  2 | 9/22/2016  | 7/11/2016  | SD     | B    |
|  2 | 2/13/2016  |            |        | A    |
|  2 | 1/1/2016   | 4/3/2016   | IT     | B    |
+----+------------+------------+--------+------+
Run Code Online (Sandbox Code Playgroud)

对于每个 ID,我需要选择 Type=A 的最小 Sdate 和 Type=B 的最大 Edate,以及与 Type=B 关联的放电原因。不止两种类型,但我以 A 和 B 为例

期望的输出:

+-------------+-----------+------------+------------------+
| Customer ID | Startdate |  Enddate   | Discharge Reason |
+-------------+-----------+------------+------------------+
|           1 | 5/30/2016 | 12/15/2016 | USD              |
|           2 | 2/13/2016 | 7/11/2016  | SD               |
+-------------+-----------+------------+------------------+
Run Code Online (Sandbox Code Playgroud)

试图:

WITH CTE AS
(
SELECT
    ID
    ,SDate
    ,EDate
    ,Reason
    ,Type
FROM T1
)

SELECT
     CTE.ID AS [Customer ID]
    ,MIN(CASE WHEN CTE.Type='A' THEN CTE.SDate END)
        OVER (PARTITION BY CTE.CID) AS StartDate
    ,CTE.EDate AS Enddate
    ,CTE.Reason AS [Discharge Reason]

FROM CTE
INNER JOIN (SELECT CTE.ID, MAX(CTE.EDate) AS EEDate 
FROM CTE WHERE CTE.Type='B' GROUP BY CTE.ID) CTE2
    ON CTE.ID=CTE2.ID AND CTE.Edate=CTE2.EEDAte
Run Code Online (Sandbox Code Playgroud)

这给了我一个空白的 Startdate 列,因为外部相关子查询上的 INNER JOIN 用于获取 MaxEdate和关联的Discharge Reason,导致查询忽略Type=A,因此没有 Startdates。我如何让这些一起工作?

Vla*_*nov 5

这是一道经典top-n-per-group题,需要做两遍。

样本数据

DECLARE @T TABLE (ID int, Sdate date, Edate date, Reason varchar(50), Type char(1));
INSERT INTO @T (ID, Sdate, Edate, Reason, Type) VALUES
(1, '2016-05-30', NULL         , NULL , 'A'),
(1, '2016-01-19', '2016-12-15' , 'USD', 'B'),
(1, '2016-11-20', '2016-10-01' , 'IT' , 'B'),
(2, '2016-10-25', NULL         , NULL , 'A'),
(2, '2016-09-22', '2016-07-11' , 'SD' , 'B'),
(2, '2016-02-13', NULL         , NULL , 'A'),
(2, '2016-01-01', '2016-04-03' , 'IT' , 'B');
Run Code Online (Sandbox Code Playgroud)

将行编号两次。

PARTITION BY ID ORDER BY Type, Sdate将为每个 和 提供最小的行号ID1 。SdateType=A

PARTITION BY ID ORDER BY Type DESC, Edate DESC将为每个 和 提供最大的行号ID1 。EdateType=B

SELECT
    ID
    ,Sdate
    ,Edate
    ,Reason
    ,Type
    ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type, Sdate) AS rnA
    ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type DESC, Edate DESC) AS rnB
FROM @T
ORDER BY ID, Type, Sdate
;
Run Code Online (Sandbox Code Playgroud)

中间结果

+----+------------+------------+--------+------+-----+-----+
| ID |   Sdate    |   Edate    | Reason | Type | rnA | rnB |
+----+------------+------------+--------+------+-----+-----+
|  1 | 2016-05-30 | NULL       | NULL   | A    |   1 |   3 |
|  1 | 2016-01-19 | 2016-12-15 | USD    | B    |   2 |   1 |
|  1 | 2016-11-20 | 2016-10-01 | IT     | B    |   3 |   2 |
|  2 | 2016-02-13 | NULL       | NULL   | A    |   1 |   3 |
|  2 | 2016-10-25 | NULL       | NULL   | A    |   2 |   4 |
|  2 | 2016-01-01 | 2016-04-03 | IT     | B    |   3 |   2 |
|  2 | 2016-09-22 | 2016-07-11 | SD     | B    |   4 |   1 |
+----+------------+------------+--------+------+-----+-----+
Run Code Online (Sandbox Code Playgroud)

现在我们需要使用rnA=1和获取行rnB=1,然后将它们连接在一起ID

询问

WITH
CTE
AS
(
    SELECT
        ID
        ,Sdate
        ,Edate
        ,Reason
        ,Type
        ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type, Sdate) AS rnA
        ,ROW_NUMBER() OVER (PARTITION BY ID ORDER BY Type DESC, Edate DESC) AS rnB
    FROM @T
)
,CTE_A
AS
(
    SELECT
        ID
        ,Sdate
    FROM CTE
    WHERE rnA = 1
)
,CTE_B
AS
(
    SELECT
        ID
        ,Edate
        ,Reason
    FROM CTE
    WHERE rnB = 1
)
SELECT
    CTE_A.ID
    ,CTE_A.Sdate
    ,CTE_B.Edate
    ,CTE_B.Reason
FROM
    CTE_A
    INNER JOIN CTE_B ON CTE_B.ID = CTE_A.ID
ORDER BY ID
;
Run Code Online (Sandbox Code Playgroud)

结果

+----+------------+------------+--------+
| ID |   Sdate    |   Edate    | Reason |
+----+------------+------------+--------+
|  1 | 2016-05-30 | 2016-12-15 | USD    |
|  2 | 2016-02-13 | 2016-07-11 | SD     |
+----+------------+------------+--------+
Run Code Online (Sandbox Code Playgroud)