Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
SAS Proc SQL获取日期最接近特定日期的记录_Sql_Date_Sas_Proc Sql - Fatal编程技术网

SAS Proc SQL获取日期最接近特定日期的记录

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) 从这两个

我有以下两个表格:

表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)
从这两个表中,我需要生成一个与表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语句中使用数据集行时,数据集行上有一个隐式循环。我将编辑答案,以便更好地说明这里发生了什么。很抱歉回复太晚。我试过了你的答案,效果很好。我把它扭曲了一点,使之适合我的需要。非常感谢你详细的回答。