我有一堆数据,其中显示了ID,最大日期及其对应的值(用户ID,类型,...)。然后我需要为每个ID取MAX日期,减去30天并显示第一个日期及其在该日期范围内的对应值。
示例:
ID Date Name 1 01.05.2018 AAA 1 21.04.2018 CCC 1 05.04.2018 BBB 1 28.03.2018 AAA
预期:
ID max_date max_name previous_date previous_name 1 01.05.2018 AAA 05.04.2018 BBB
我有使用子选择的有效解决方案,但是由于我的WHERE部分相当庞大,刷新需要一段时间。
SUBSELECT看起来像这样:
(SELECT MIN(N.name)
从t1 N开始
N.ID = T.ID
AND(N.date
您如何编写选择?
我正在使用TSQL
谢谢
答案 0 :(得分:1)
我可以使用2个CTE来建立日期和名称。
MS SQL Server 2017架构设置:
CREATE TABLE t1 (ID int, theDate date, theName varchar(10)) ;
INSERT INTO t1 (ID, theDate, theName)
VALUES
( 1,'2018-05-01','AAA' )
, ( 1,'2018-04-21','CCC' )
, ( 1,'2018-04-05','BBB' )
, ( 1,'2018-03-27','AAA' )
, ( 2,'2018-05-02','AAA' )
, ( 2,'2018-05-21','CCC' )
, ( 2,'2018-03-03','BBB' )
, ( 2,'2018-01-20','AAA' )
;
主要查询:
;WITH cte1 AS (
SELECT t1.ID, t1.theDate, t1.theName
, DATEADD(day,-30,t1.theDate) AS dMinus30
, ROW_NUMBER() OVER (PARTITION BY t1.ID ORDER BY t1.theDate DESC) AS rn
FROM t1
)
, cte2 AS (
SELECT c2.ID, c2.theDate, c2.theName
, ROW_NUMBER() OVER (PARTITION BY c2.ID ORDER BY c2.theDate) AS rn
, COUNT(*) OVER (PARTITION BY c2.ID) AS theCount
FROM cte1
INNER JOIN cte1 c2 ON cte1.ID = c2.ID
AND c2.theDate >= cte1.dMinus30
WHERE cte1.rn = 1
GROUP BY c2.ID, c2.theDate, c2.theName
)
SELECT cte1.ID, cte1.theDate AS max_date, cte1.theName AS max_name
, cte2.theDate AS previous_date, cte2.theName AS previous_name
, cte2.theCount
FROM cte1
INNER JOIN cte2 ON cte1.ID = cte2.ID
AND cte2.rn=1
WHERE cte1.rn = 1
Results :
| ID | max_date | max_name | previous_date | previous_name |
|----|------------|----------|---------------|---------------|
| 1 | 2018-05-01 | AAA | 2018-04-05 | BBB |
| 2 | 2018-05-21 | CCC | 2018-05-02 | AAA |
cte1
构建按max_date
分组的max_name
和ID
的列表,然后使用ROW_NUMBER()
窗口函数按日期对分组进行排序,以获取最新日期。 cte2
返回此列表,以获取cte1
的最大日期的最近30天内的所有日期。然后,它执行相同的操作来获取最后一个日期。然后,外部查询将这两个结果结合在一起,以获取所需的列,而仅分别从每个行中选择最新和最近的行。
我不确定它将如何随您的数据扩展,但是使用CTE应该可以很好地进行优化。
编辑:出于其他要求,我刚刚在COUNT()
中添加了另一个cte2
窗口函数。
答案 1 :(得分:0)
我会做的:
select id,
max(case when seqnum = 1 then date end) as max_date,
max(case when seqnum = 1 then name end) as max_name,
max(case when seqnum = 2 then date end) as prev_date,
max(case when seqnum = 2 then name end) as prev_name,
from (select e.*, row_number() over (partition by id order by date desc) as seqnum
from example e
) e
group by id;