Tsql 最小日期和最大日期的条件行号()
我得到了一个包含如下数据的表: 表T1Tsql 最小日期和最大日期的条件行号(),tsql,date,conditional,common-table-expression,row-number,Tsql,Date,Conditional,Common Table Expression,Row Number,我得到了一个包含如下数据的表: 表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
+----+------------+------------+
| 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在不同的日期有多个代码,并且没有模式
总的来说,我正在尝试获取最后一个日期和代码,但如果有尝试代码,我需要获取每个ID的第一个日期和代码。根据上表,我将获得:
+----+------------+------------+
| 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
我对CTEs不太熟悉,我认为这是一个可能需要的问题
谢谢。在尝试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]);
为了解释我是如何到达那里的,主要是基于您的要求:
总的来说,我试图得到最后的日期和代码,但如果有
尝试代码,我需要获得第一个日期和每个日期的代码
个人身份证
因此,对于每个ID,我都需要一种方法来获取:
最后一个代码的最小Udate=每个ID的“尝试”-如果没有尝试,我们将得到一个空值
每个ID的所有记录的最大Udate
如果我可以根据ID确定每个记录的上述值,那么我的最终结果集基本上就是那些最小值为空时,Udate等于最大Udate的结果集。如果最小值不是null,则使用该值
第一个选项是使用2个外部应用程序来执行上面的每一点
最后一个代码的最小Udate=每个ID的“尝试”-如果没有尝试,我们将得到空值:
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]
MIN( CASE WHEN [last_code] = 'ATTEMPT' THEN [Udate]
ELSE NULL
END
) OVER ( PARTITION BY [ID] ) AS [AttemptUdate]
外部应用,因为我可能没有给定ID的尝试记录,所以在这些情况下它返回NULL
每个ID的所有记录的最大Udate:
OUTER APPLY (
SELECT [c].[ID]
, MAX([c].[Udate]) AS [MaxUdate]
FROM @TestData [c]
WHERE [c].[ID] = [a].[ID]
GROUP BY [c].[ID]
) AS [cc]
MAX([Udate]) OVER ( PARTITION BY [ID] ) AS [MaxUdate]
然后where子句比较那些返回的内容,只返回我想要的记录:
[a].[Udate] = COALESCE([aa].[AttemptUdate], [cc].[MaxUdate]);
我使用COALESCE来处理和计算空值。COALESCE将从左到右计算字段,并使用/返回第一个非空值
因此,将其与Udate一起使用,我们可以评估我应该在过滤器中使用哪个Udate值来满足要求
因为如果我有一个尝试记录字段,attempudate将有一个值并首先在过滤器中使用。如果我没有尝试记录,那么attempudate将为NULL,因此将使用MaxUdate
对于选项2,相似只是后面有点不同
最后一个代码的最小Udate=每个ID的“尝试”-如果没有尝试,我们将得到空值:
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]
MIN( CASE WHEN [last_code] = 'ATTEMPT' THEN [Udate]
ELSE NULL
END
) OVER ( PARTITION BY [ID] ) AS [AttemptUdate]
在Udate上输入Min,但我使用case语句来评估该记录是否是一次尝试。使用OVER PARTITION将根据我告诉它如何按ID对数据进行分区来实现这一点
每个ID的所有记录的最大Udate:
OUTER APPLY (
SELECT [c].[ID]
, MAX([c].[Udate]) AS [MaxUdate]
FROM @TestData [c]
WHERE [c].[ID] = [a].[ID]
GROUP BY [c].[ID]
) AS [cc]
MAX([Udate]) OVER ( PARTITION BY [ID] ) AS [MaxUdate]
去给我一个基于ID的最大Udate,因为我是这样告诉它分区的
我在子查询中完成了所有这些,以使where子句更易于使用。那么过滤的时候跟以前一样:
[td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
使用COALESCE来确定我应该使用哪个日期,并且只返回我想要的记录
对于第二个选项,再深入一点,如果只运行子查询,您将看到每个记录都有需求的两个主要驱动点:
每个ID的最大Udate是多少
上次代码=每个ID的尝试次数的最小值是多少
从那里,我可以只过滤那些满足我最初寻找的记录,使用合并来简化我的过滤器
[td].[Udate] = COALESCE([td].[AttemptUdate], [td].[MaxUdate]);
使用attempudate,除非它为NULL,否则使用MaxUdate。我不确定行号在哪里与您正在尝试的内容起作用。如果需要,你澄清一下,我可以在下面调整我的答案,如果这不能满足你的需要。我使用的是行号,因为一开始我没有意识到可能有多个“尝试”代码。它比最大和最小日期的内部连接快得多。它工作得很好!!!我有一些副本,但这是我的数据,不是你的查询。谢谢+1。另外,如果你能简单地解释一下这个想法的过程,我会非常感激,我想看看我遗漏了什么。谢谢。@madlicksxxx当然!我更新了答案,添加了我的思考过程以及我是如何完成的。希望能有帮助。