SELECT MIN Date(使用WINDOW函数)和MAX Date(使用JOIN的相关字段)在一起

时间:2017-01-05 07:11:27

标签: sql sql-server sql-server-2012 greatest-n-per-group window-functions

表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    |
+----+------------+------------+--------+------+

对于每个ID,我需要为Type = A选择最小Sdate,为Type = B选择最大Edate,以及与Type = B关联的Discharge Reason。不仅仅有两种类型,但我已将A和B显示为一个例子

期望输出:

+-------------+-----------+------------+------------------+
| Customer ID | Startdate |  Enddate   | Discharge Reason |
+-------------+-----------+------------+------------------+
|           1 | 5/30/2016 | 12/15/2016 | USD              |
|           2 | 2/13/2016 | 7/11/2016  | SD               |
+-------------+-----------+------------+------------------+

尝试:

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

这给了我一个空白的Startdate列,因为外部联合相关子查询上的INNER JOIN用于获取Max Edate和关联的Discharge Reason,导致查询忽略{{1}因此没有开始。我如何将这些工作结合在一起?

2 个答案:

答案 0 :(得分:1)

这是一个经典的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');

对行进行两次编号。

PARTITION BY ID ORDER BY Type, Sdate会为每个ID和最小Sdate以及Type=A提供第1行。

PARTITION BY ID ORDER BY Type DESC, Edate DESC会为每个ID以及最大EdateType=B提供第1行。

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
;

中级结果

+----+------------+------------+--------+------+-----+-----+
| 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 |
+----+------------+------------+--------+------+-----+-----+

现在,我们需要获取包含rnA=1rnB=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
;

<强>结果

+----+------------+------------+--------+
| ID |   Sdate    |   Edate    | Reason |
+----+------------+------------+--------+
|  1 | 2016-05-30 | 2016-12-15 | USD    |
|  2 | 2016-02-13 | 2016-07-11 | SD     |
+----+------------+------------+--------+

答案 1 :(得分:0)

你可以试试这个:

SELECT
    t.ID
    ,t.SDate
    ,t.EDate
    ,Reason
FROM (
SELECT  ID, MIN(CASE WHEN Type='A' THEN SDate END) as SDate , max(CASE WHEN Type='B' THEN EDate END ) as EDate
FROM T1
group by ID) t 
INNER JOIN T1 on t.ID = T1.id and t.EDate = T1.Edate and type = 'B'

OR

WITH CTE AS
(
SELECT  ID, MIN(CASE WHEN Type='A' THEN SDate END) as SDate , max(CASE WHEN Type='B' THEN EDate END ) as EDate
FROM T1
group by ID
)
SELECT
     CTE.ID AS [Customer ID]
    ,CTE.SDate AS StartDate
    ,CTE.EDate AS Enddate
    ,T1.Reason AS [Discharge Reason]

FROM CTE
INNER JOIN T1 
    ON CTE.ID=T1.ID AND CTE.Edate=T1.EDAte and T1.type = 'B'

如果B类型没有重复的最大日期,如果有,则需要稍微调整一下。