最小和最大日期的条件 Row_Number()
Conditional Row_Number() for min and maximum date
我有一个 table 数据如下所示:
Table T1
+----+------------+------------+
| ID | Udate | last_code |
+----+------------+------------+
| 1 | 05/11/2018 | ATTEMPT |
| 1 | 03/11/2018 | ATTEMPT |
| 1 | 01/11/2017 | INFO |
| 1 | 25/10/2016 | ARRIVED |
| 1 | 22/9/2016 | ARRIVED |
| 1 | 14/9/2016 | SENT |
| 1 | 1/9/2016 | SENT |
+----+------------+------------+
| 2 | 26/10/2016 | RECEIVED |
| 2 | 19/10/2016 | ARRIVED |
| 2 | 18/10/2016 | ARRIVED |
| 2 | 14/10/2016 | ANNOUNCED |
| 2 | 23/9/2016 | INFO |
| 2 | 14/9/2016 | DAMAGE |
| 2 | 2/9/2016 | SCHEDULED |
+----+------------+------------+
每个 id 在不同的日期有多个代码,并且没有它们的模式。
总的来说,我正在尝试获取最后的日期和代码,但是 如果 有一个 "ATTEMPT" 代码,我需要获取第一个日期和该代码每个单独的 ID。根据上面的 table,我会得到:
+----+------------+------------+
| ID | Udate | last_code |
| 1 | 03/11/2018 | ATTEMPT |
| 2 | 26/10/2016 | RECEIVED |
+----+------------+------------+
我一直在努力
ROW_NUMBER() OVER (PARTITION BY ID
ORDER BY
(CASE WHEN code = 'ATTEMPT' THEN u_date END) ASC,
(CASE WHEN code_key <> 'ATTEMPT' THEN u_date END) DESC
) as RN
目前我在使用 ROW_NUMBER() 两次后卡住了,但想不出一种方法将它们全部放在同一个 table.
,ROW_NUMBER() OVER (PARTITION BY id, code order by udate asc) as RN1
,ROW_NUMBER() OVER (PARTITION BY id order by udate desc) AS RN2
我对 CTE 不是很熟悉,我认为这是可能需要一个的查询之一..
谢谢。
我认为在尝试 CTE 之前您有几个选择。
试试看,示例如下:
DECLARE @TestData TABLE
(
[ID] INT
, [Udate] DATE
, [last_code] NVARCHAR(100)
);
INSERT INTO @TestData (
[ID]
, [Udate]
, [last_code]
)
VALUES ( 1, '11/05/2018', 'ATTEMPT ' )
, ( 1, '11/03/2018', 'ATTEMPT' )
, ( 1, '11/01/2017', 'INFO' )
, ( 1, '10/25/2016', 'ARRIVED' )
, ( 1, '9/22/2016 ', 'ARRIVED' )
, ( 1, '9/14/2016 ', 'SENT' )
, ( 1, '9/1/2016 ', 'SENT' )
, ( 2, '10/26/2016', 'RECEIVED' )
, ( 2, '10/19/2016', 'ARRIVED' )
, ( 2, '10/18/2016', 'ARRIVED' )
, ( 2, '10/14/2016', 'ANNOUNCED' )
, ( 2, '9/23/2016 ', 'INFO' )
, ( 2, '9/14/2016 ', 'DAMAGE' )
, ( 2, '9/2/2016 ', 'SCHEDULED' );
--option 1
--couple of outer apply
--1 - to get the min date for attempt
--2 - to get the max date regardless of the the code
--where clause, using coalesce will pick what date. Use the date if I have one for code ='ATTEMPT', if not use the max date.
SELECT [a].*
FROM @TestData [a]
OUTER APPLY (
SELECT [b].[ID]
, MIN([b].[Udate]) AS [AttemptUdate]
FROM @TestData [b]
WHERE [b].[ID] = [a].[ID]
AND [b].[last_code] = 'ATTEMPT'
GROUP BY [b].[ID]
) AS [aa]
OUTER APPLY (
SELECT [c].[ID]
, MAX([c].[Udate]) AS [MaxUdate]
FROM @TestData [c]
WHERE [c].[ID] = [a].[ID]
GROUP BY [c].[ID]
) AS [cc]
WHERE [a].[ID] = COALESCE([aa].[ID], [cc].[ID])
AND [a].[Udate] = COALESCE([aa].[AttemptUdate], [cc].[MaxUdate]);
--use window functions
--Similiar in that we are finding the max Udate and also min Udate when last_code='ATTEMPT'
--Then using COALESCE in the where clause to evaluate which one to use.
--Maybe a little cleaner
SELECT [td].[ID]
, [td].[Udate]
, [td].[last_code]
FROM (
SELECT [ID]
, [last_code]
, [Udate]
, MAX([Udate]) OVER ( PARTITION BY [ID] ) AS [MaxUdate]
, MIN( CASE WHEN [last_code] = 'ATTEMPT' THEN [Udate]
ELSE NULL
END
) OVER ( PARTITION BY [ID] ) AS [AttemptUdate]
FROM @TestData
) AS [td]
WHERE [td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
稍微解释一下我是如何到达那里的,主要是根据您的要求:
Overall I m trying to get the last date and code, but if there is an
"ATTEMPT" code, I need to get the first date and that code for each
individual ID.
所以对于每个 ID,我需要一种方法来获取:
- 每个 ID last_code = 'ATTEMPT' 的最小 Udate - 如果没有 ATTEMPT,我们将得到一个 null
- 每个 ID 的所有记录的最大 Udate
如果我可以根据 ID 确定每条记录的上述内容,那么我的最终结果集基本上是那些 Udate 等于我的 Maximum Udate(如果 Minimum 为空)的结果集。如果最小值不为空,请改用它。
第一个选项,使用 2 个外部应用来执行上述每一点。
每个 ID 的 last_code = 'ATTEMPT' 的最小更新 - 如果没有 ATTEMPT,我们将得到一个空值:
OUTER APPLY (
SELECT [b].[ID]
, MIN([b].[Udate]) AS [AttemptUdate]
FROM @TestData [b]
WHERE [b].[ID] = [a].[ID]
AND [b].[last_code] = 'ATTEMPT'
GROUP BY [b].[ID]
) AS [aa]
外部应用,因为我可能没有给定 ID 的 ATTEMPT 记录,所以在那些情况下它 return 为 NULL。
每个 ID 所有记录的最大更新日期:
OUTER APPLY (
SELECT [c].[ID]
, MAX([c].[Udate]) AS [MaxUdate]
FROM @TestData [c]
WHERE [c].[ID] = [a].[ID]
GROUP BY [c].[ID]
) AS [cc]
然后 where 子句将那些 return 编辑的内容与 return 只有我想要的记录进行比较:
[a].[Udate] = COALESCE([aa].[AttemptUdate], [cc].[MaxUdate]);
我正在使用 COALESCE 来处理和评估 NULL。 COALESCE 将从左到右评估字段,use/return 第一个非 NULL 值。
所以将它与 Udate 一起使用,我们可以评估我应该在我的过滤器中使用哪个 Udate 值来满足要求。
因为如果我有一个 ATTEMPT 记录字段 AttemptUdate 将有一个值并首先在过滤器中使用。如果我没有 ATTEMPT 记录,AttemptUdate 将为 NULL,那么将使用 MaxUdate。
对于选项 2,相似只是稍有不同。
每个 ID 的 last_code = 'ATTEMPT' 的最小更新 - 如果没有 ATTEMPT,我们将得到一个空值:
MIN( CASE WHEN [last_code] = 'ATTEMPT' THEN [Udate]
ELSE NULL
END
) OVER ( PARTITION BY [ID] ) AS [AttemptUdate]
Min on Udate,但我使用 case 语句来评估该记录是否为 ATTEMPT。使用 OVER PARTITION 将根据我告诉它如何按 ID 对数据进行分区来做到这一点。
每个 ID 所有记录的最大更新日期:
MAX([Udate]) OVER ( PARTITION BY [ID] ) AS [MaxUdate]
根据 ID 给我最大的 Udate,因为这是我告诉它分区的方式。
我在子查询中执行所有这些操作,以使 where 子句更易于使用。然后过滤的时候和之前一样:
[td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
使用 COALESCE 来确定我应该使用哪个日期,并且只 return 我想要的记录。
对于第二个选项,更深入一点,如果您 运行 只是子查询,您会看到您为每个单独的记录获得了需求的 2 个主要驱动点:
- 每个 ID 的最大更新时间是多少
- 每个 IDlast_code=ATTEMPT 的最小 Udate 是多少
从那里我可以过滤那些满足我最初寻找的记录,使用 COALESCE 来简化我的过滤器。
[td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
使用 AttemptUdate,除非它为 NULL 然后使用 MaxUdate。
我有一个 table 数据如下所示:
Table T1
+----+------------+------------+
| ID | Udate | last_code |
+----+------------+------------+
| 1 | 05/11/2018 | ATTEMPT |
| 1 | 03/11/2018 | ATTEMPT |
| 1 | 01/11/2017 | INFO |
| 1 | 25/10/2016 | ARRIVED |
| 1 | 22/9/2016 | ARRIVED |
| 1 | 14/9/2016 | SENT |
| 1 | 1/9/2016 | SENT |
+----+------------+------------+
| 2 | 26/10/2016 | RECEIVED |
| 2 | 19/10/2016 | ARRIVED |
| 2 | 18/10/2016 | ARRIVED |
| 2 | 14/10/2016 | ANNOUNCED |
| 2 | 23/9/2016 | INFO |
| 2 | 14/9/2016 | DAMAGE |
| 2 | 2/9/2016 | SCHEDULED |
+----+------------+------------+
每个 id 在不同的日期有多个代码,并且没有它们的模式。
总的来说,我正在尝试获取最后的日期和代码,但是 如果 有一个 "ATTEMPT" 代码,我需要获取第一个日期和该代码每个单独的 ID。根据上面的 table,我会得到:
+----+------------+------------+
| ID | Udate | last_code |
| 1 | 03/11/2018 | ATTEMPT |
| 2 | 26/10/2016 | RECEIVED |
+----+------------+------------+
我一直在努力
ROW_NUMBER() OVER (PARTITION BY ID
ORDER BY
(CASE WHEN code = 'ATTEMPT' THEN u_date END) ASC,
(CASE WHEN code_key <> 'ATTEMPT' THEN u_date END) DESC
) as RN
目前我在使用 ROW_NUMBER() 两次后卡住了,但想不出一种方法将它们全部放在同一个 table.
,ROW_NUMBER() OVER (PARTITION BY id, code order by udate asc) as RN1
,ROW_NUMBER() OVER (PARTITION BY id order by udate desc) AS RN2
我对 CTE 不是很熟悉,我认为这是可能需要一个的查询之一..
谢谢。
我认为在尝试 CTE 之前您有几个选择。
试试看,示例如下:
DECLARE @TestData TABLE
(
[ID] INT
, [Udate] DATE
, [last_code] NVARCHAR(100)
);
INSERT INTO @TestData (
[ID]
, [Udate]
, [last_code]
)
VALUES ( 1, '11/05/2018', 'ATTEMPT ' )
, ( 1, '11/03/2018', 'ATTEMPT' )
, ( 1, '11/01/2017', 'INFO' )
, ( 1, '10/25/2016', 'ARRIVED' )
, ( 1, '9/22/2016 ', 'ARRIVED' )
, ( 1, '9/14/2016 ', 'SENT' )
, ( 1, '9/1/2016 ', 'SENT' )
, ( 2, '10/26/2016', 'RECEIVED' )
, ( 2, '10/19/2016', 'ARRIVED' )
, ( 2, '10/18/2016', 'ARRIVED' )
, ( 2, '10/14/2016', 'ANNOUNCED' )
, ( 2, '9/23/2016 ', 'INFO' )
, ( 2, '9/14/2016 ', 'DAMAGE' )
, ( 2, '9/2/2016 ', 'SCHEDULED' );
--option 1
--couple of outer apply
--1 - to get the min date for attempt
--2 - to get the max date regardless of the the code
--where clause, using coalesce will pick what date. Use the date if I have one for code ='ATTEMPT', if not use the max date.
SELECT [a].*
FROM @TestData [a]
OUTER APPLY (
SELECT [b].[ID]
, MIN([b].[Udate]) AS [AttemptUdate]
FROM @TestData [b]
WHERE [b].[ID] = [a].[ID]
AND [b].[last_code] = 'ATTEMPT'
GROUP BY [b].[ID]
) AS [aa]
OUTER APPLY (
SELECT [c].[ID]
, MAX([c].[Udate]) AS [MaxUdate]
FROM @TestData [c]
WHERE [c].[ID] = [a].[ID]
GROUP BY [c].[ID]
) AS [cc]
WHERE [a].[ID] = COALESCE([aa].[ID], [cc].[ID])
AND [a].[Udate] = COALESCE([aa].[AttemptUdate], [cc].[MaxUdate]);
--use window functions
--Similiar in that we are finding the max Udate and also min Udate when last_code='ATTEMPT'
--Then using COALESCE in the where clause to evaluate which one to use.
--Maybe a little cleaner
SELECT [td].[ID]
, [td].[Udate]
, [td].[last_code]
FROM (
SELECT [ID]
, [last_code]
, [Udate]
, MAX([Udate]) OVER ( PARTITION BY [ID] ) AS [MaxUdate]
, MIN( CASE WHEN [last_code] = 'ATTEMPT' THEN [Udate]
ELSE NULL
END
) OVER ( PARTITION BY [ID] ) AS [AttemptUdate]
FROM @TestData
) AS [td]
WHERE [td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
稍微解释一下我是如何到达那里的,主要是根据您的要求:
Overall I m trying to get the last date and code, but if there is an "ATTEMPT" code, I need to get the first date and that code for each individual ID.
所以对于每个 ID,我需要一种方法来获取:
- 每个 ID last_code = 'ATTEMPT' 的最小 Udate - 如果没有 ATTEMPT,我们将得到一个 null
- 每个 ID 的所有记录的最大 Udate
如果我可以根据 ID 确定每条记录的上述内容,那么我的最终结果集基本上是那些 Udate 等于我的 Maximum Udate(如果 Minimum 为空)的结果集。如果最小值不为空,请改用它。
第一个选项,使用 2 个外部应用来执行上述每一点。
每个 ID 的 last_code = 'ATTEMPT' 的最小更新 - 如果没有 ATTEMPT,我们将得到一个空值:
OUTER APPLY (
SELECT [b].[ID]
, MIN([b].[Udate]) AS [AttemptUdate]
FROM @TestData [b]
WHERE [b].[ID] = [a].[ID]
AND [b].[last_code] = 'ATTEMPT'
GROUP BY [b].[ID]
) AS [aa]
外部应用,因为我可能没有给定 ID 的 ATTEMPT 记录,所以在那些情况下它 return 为 NULL。
每个 ID 所有记录的最大更新日期:
OUTER APPLY (
SELECT [c].[ID]
, MAX([c].[Udate]) AS [MaxUdate]
FROM @TestData [c]
WHERE [c].[ID] = [a].[ID]
GROUP BY [c].[ID]
) AS [cc]
然后 where 子句将那些 return 编辑的内容与 return 只有我想要的记录进行比较:
[a].[Udate] = COALESCE([aa].[AttemptUdate], [cc].[MaxUdate]);
我正在使用 COALESCE 来处理和评估 NULL。 COALESCE 将从左到右评估字段,use/return 第一个非 NULL 值。
所以将它与 Udate 一起使用,我们可以评估我应该在我的过滤器中使用哪个 Udate 值来满足要求。
因为如果我有一个 ATTEMPT 记录字段 AttemptUdate 将有一个值并首先在过滤器中使用。如果我没有 ATTEMPT 记录,AttemptUdate 将为 NULL,那么将使用 MaxUdate。
对于选项 2,相似只是稍有不同。
每个 ID 的 last_code = 'ATTEMPT' 的最小更新 - 如果没有 ATTEMPT,我们将得到一个空值:
MIN( CASE WHEN [last_code] = 'ATTEMPT' THEN [Udate]
ELSE NULL
END
) OVER ( PARTITION BY [ID] ) AS [AttemptUdate]
Min on Udate,但我使用 case 语句来评估该记录是否为 ATTEMPT。使用 OVER PARTITION 将根据我告诉它如何按 ID 对数据进行分区来做到这一点。
每个 ID 所有记录的最大更新日期:
MAX([Udate]) OVER ( PARTITION BY [ID] ) AS [MaxUdate]
根据 ID 给我最大的 Udate,因为这是我告诉它分区的方式。
我在子查询中执行所有这些操作,以使 where 子句更易于使用。然后过滤的时候和之前一样:
[td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
使用 COALESCE 来确定我应该使用哪个日期,并且只 return 我想要的记录。
对于第二个选项,更深入一点,如果您 运行 只是子查询,您会看到您为每个单独的记录获得了需求的 2 个主要驱动点:
- 每个 ID 的最大更新时间是多少
- 每个 IDlast_code=ATTEMPT 的最小 Udate 是多少
从那里我可以过滤那些满足我最初寻找的记录,使用 COALESCE 来简化我的过滤器。
[td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
使用 AttemptUdate,除非它为 NULL 然后使用 MaxUdate。