SAS Proc SQL获取日期最接近特定日期的记录
我有以下两个表格: 表1,用户列表表:SAS Proc SQL获取日期最接近特定日期的记录,sql,date,sas,proc-sql,Sql,Date,Sas,Proc Sql,我有以下两个表格: 表1,用户列表表: Year Month Id Type 2010 3 1 A 2010 5 2 B 2010 10 1 A 2010 12 1 A 表2描述了用户升级历史记录: Promote Date Id 2/20/2010 1 5/20/2010 1 (4/2010 the user got demoted, and after 1 month he got promote again) 从这两个
Year Month Id Type
2010 3 1 A
2010 5 2 B
2010 10 1 A
2010 12 1 A
表2描述了用户升级历史记录:
Promote Date Id
2/20/2010 1
5/20/2010 1 (4/2010 the user got demoted, and after 1 month he got promote again)
从这两个表中,我需要生成一个与表1类似的结果表,但添加一个列,该列对在过去3个月或在特定日期超过3个月内提升的类型为a的用户进行分类。例如,结果将是:
Year Month Id | Duration
2010 3 1 | A < 3 months
2010 10 1 | A > 3 months
2010 12 1 | A > 3 months
总的想法是:
我需要将表1中的月份列和年份列转换为
日期格式如2010年3月
将新转换的值与最接近的升级日期相减
以上日期2010年2月,以获取用户已访问的天数
促进
比较90天来分类他的晋升时间
我目前有两个问题
我不知道将月列和年列转换为月/年日期格式的最佳方法
假设我已经转换了表1中的月/年列,我使用Max函数从表2中获取最近的日期。据我所知,max函数对性能不好,所以有没有其他解决方案代替max?在mysql中,使用Limit 1很容易解决问题,但SAS proc sql不支持Limit。proc-sql中是否有与limit等效的限制?下面是我目前正在考虑的代码
PROC SQL;
Create table Result as SELECT table1.Year, table1.Month, table1.Code,
(Case When table1.Type = "B" then "B"
When table1.Type = "A" AND (table1.Date - (Select MAX(table2.Date) From table2 Where table2.Date <= table1.Date AND table2.Id = table1.Id ) < 90) THEN "A < 3 months"
When table1.Type = "A" AND (table1.Date - (Select MAX(table2.Date) From table2 Where table2.Date <= table1.Date AND table2.Id = table1.Id ) >= 90) THEN "A > 3 months"
When table1.Type = "C" then "C"
end) as NewType
From table1
LEFT JOIN
// ....
;
QUIT;
如您所见,我需要将table1与其他表左连接,因此我使用子查询,这也是一种糟糕的性能,但我不知道是否还有其他方法。感谢您的帮助和建议。您可以使用mdy函数创建日期值,如下所示:
data have;
input Year Month Id Type $;
datalines;
2010 3 1 A
2010 5 2 B
2010 10 1 A
2010 12 1 A
;
run;
data have;
set have;
format date date9.;
date = mdy(Month, 1, Year);
run;
Year Month Id Type date Promote diff
---------------------------------------------------
2010 3 1 A 01MAR2010 20FEB2010 9
2010 3 1 A 01MAR2010 20MAY2010 80
2010 5 2 B 01MAY2010 . .
2010 10 1 A 01OCT2010 20MAY2010 134
2010 10 1 A 01OCT2010 20FEB2010 223
2010 12 1 A 01DEC2010 20MAY2010 195
2010 12 1 A 01DEC2010 20FEB2010 284
您没有day值,所以我只使用了1,每个创建的日期都是一个月的第一天
现在,您可以通过ID连接两个表,并计算第一个表中的日期与第二个表中的促销日期之间的差异:
proc sql;
create table want as
select *
,abs(date - promote) as diff
from have as a
left join
prom as b
on a.id = b.id;
quit;
然后,按id、日期和差异对结果表进行排序:
proc sort data=want;
by id date diff;
run;
排序后的数据集如下所示:
data have;
input Year Month Id Type $;
datalines;
2010 3 1 A
2010 5 2 B
2010 10 1 A
2010 12 1 A
;
run;
data have;
set have;
format date date9.;
date = mdy(Month, 1, Year);
run;
Year Month Id Type date Promote diff
---------------------------------------------------
2010 3 1 A 01MAR2010 20FEB2010 9
2010 3 1 A 01MAR2010 20MAY2010 80
2010 5 2 B 01MAY2010 . .
2010 10 1 A 01OCT2010 20MAY2010 134
2010 10 1 A 01OCT2010 20FEB2010 223
2010 12 1 A 01DEC2010 20MAY2010 195
2010 12 1 A 01DEC2010 20FEB2010 284
最后一步,遍历数据集,检查每个ID和日期值的第一个差异值是否小于或大于我刚刚检查的3个月的90天,您也可以使用intck函数。因为我们按id、日期和差异对数据集进行了排序,所以第一行应该与日期最接近,所以只输出第一行
data want2(keep = year month id type duration);
set want;
by date;
if first.date and Type = 'A' then do;
if diff lt 90 then do;
duration = 'A < 3 months';
output want2;
end;
if diff gt 90 then do;
duration = 'A > 3 months';
output want2;
end;
end;
else if first.date then do;
duration = type;
output want2;
end;
run;
您可以使用mdy函数从其创建日期值,如下所示:
data have;
input Year Month Id Type $;
datalines;
2010 3 1 A
2010 5 2 B
2010 10 1 A
2010 12 1 A
;
run;
data have;
set have;
format date date9.;
date = mdy(Month, 1, Year);
run;
Year Month Id Type date Promote diff
---------------------------------------------------
2010 3 1 A 01MAR2010 20FEB2010 9
2010 3 1 A 01MAR2010 20MAY2010 80
2010 5 2 B 01MAY2010 . .
2010 10 1 A 01OCT2010 20MAY2010 134
2010 10 1 A 01OCT2010 20FEB2010 223
2010 12 1 A 01DEC2010 20MAY2010 195
2010 12 1 A 01DEC2010 20FEB2010 284
您没有day值,所以我只使用了1,每个创建的日期都是一个月的第一天
现在,您可以通过ID连接两个表,并计算第一个表中的日期与第二个表中的促销日期之间的差异:
proc sql;
create table want as
select *
,abs(date - promote) as diff
from have as a
left join
prom as b
on a.id = b.id;
quit;
然后,按id、日期和差异对结果表进行排序:
proc sort data=want;
by id date diff;
run;
排序后的数据集如下所示:
data have;
input Year Month Id Type $;
datalines;
2010 3 1 A
2010 5 2 B
2010 10 1 A
2010 12 1 A
;
run;
data have;
set have;
format date date9.;
date = mdy(Month, 1, Year);
run;
Year Month Id Type date Promote diff
---------------------------------------------------
2010 3 1 A 01MAR2010 20FEB2010 9
2010 3 1 A 01MAR2010 20MAY2010 80
2010 5 2 B 01MAY2010 . .
2010 10 1 A 01OCT2010 20MAY2010 134
2010 10 1 A 01OCT2010 20FEB2010 223
2010 12 1 A 01DEC2010 20MAY2010 195
2010 12 1 A 01DEC2010 20FEB2010 284
最后一步,遍历数据集,检查每个ID和日期值的第一个差异值是否小于或大于我刚刚检查的3个月的90天,您也可以使用intck函数。因为我们按id、日期和差异对数据集进行了排序,所以第一行应该与日期最接近,所以只输出第一行
data want2(keep = year month id type duration);
set want;
by date;
if first.date and Type = 'A' then do;
if diff lt 90 then do;
duration = 'A < 3 months';
output want2;
end;
if diff gt 90 then do;
duration = 'A > 3 months';
output want2;
end;
end;
else if first.date then do;
duration = type;
output want2;
end;
run;
非常感谢您的详细回答。关于日值,有没有办法将日值设为一个月的最后一天?最后一个代码块,你能告诉我迭代的命令在哪里吗?如果我们使用output命令,它会创建所需的结果表吗?您可以使用intnx函数将日期值移动到月末,如下所示:intnx'month',mdyMonth,1,Year,0,'end'。不需要使用“iterate”命令,在set语句中使用数据集行时,数据集行上有一个隐式循环。我将编辑答案,以便更好地说明这里发生了什么。很抱歉回复太晚。我试过了你的答案,效果很好。我把它扭曲了一点,使之适合我的需要。非常感谢你的详细回答。非常感谢你的详细回答。关于日值,有没有办法将日值设为一个月的最后一天?最后一个代码块,你能告诉我迭代的命令在哪里吗?如果我们使用output命令,它会创建所需的结果表吗?您可以使用intnx函数将日期值移动到月末,如下所示:intnx'month',mdyMonth,1,Year,0,'end'。不需要使用“iterate”命令,在set语句中使用数据集行时,数据集行上有一个隐式循环。我将编辑答案,以便更好地说明这里发生了什么。很抱歉回复太晚。我试过了你的答案,效果很好。我把它扭曲了一点,使之适合我的需要。非常感谢你详细的回答。