我通过SAS从SQL Server中提取大量数据。我想使用循环一次拉一分钟(或小时)的数据。
时间戳的格式为' yyyymmdd hh:mm:ss.000'。
通常,我会做类似的事情:%macro Loop(num_days, tmstmp_begin):
%do i = 0 to &num_days.;
proc sql;
...
where tmstmp between &tmstmp_begin + &i minutes and &tmstmp_begin (&i+1) minutes;
quit;
%end
%mend;
但是服务器上不支持分钟添加。在Teradata,我可以使用:
DATEADD(minute, 1, tmsmtmp)
在时间戳中添加一分钟,但这不会在SAS中执行(它不会传递到服务器?):
ERROR: Function DATEADD could not be located.
无论如何,我想知道是否有一个简洁的%sysfunc
解决方案可以帮助我避免生成一个时间戳的SAS表,我会从中读取宏变量,或者其他一些同样愚蠢的东西。
基本上我需要从:
%let i = 1;
%let tmstmp = '20150801 00:00:00.000'
为:
%put ...something involving tmstmp and i...;
'20150801 00:01:00.000'
谢谢!
答案 0 :(得分:2)
我发现处理这些方案的最佳方法是使用自定义日期时间格式。您可以找到有关构建它们的链接here。我建议将格式保存到公共库,以便它始终可用于SAS会话。格式为:
proc format ;
picture mssqldt low-high = '''%Y-%0m-%0d %0H:%0M:%0S.000''' (datatype = datetime) ;
run ;
这将采用常规SAS日期时间戳并将其格式化(包括引号):
'2015-09-21 15:04:16.000'
将此功能合并到SAS代码中的最佳方法是始终将日期和日期时间保留在SAS表示形式中,并为SQL Server格式化变量分别设置变量。 E.g。
计算我们想要使用的日期时间:
%let my_datetime = %sysfunc(datetime());
使用SQL Server格式化的日期时间戳创建两个新变量。我总是称我的&sql_start
和&sql_end
,以便他们读得很好,我永远不必考虑它......
%let sql_start = %sysfunc(sum(&my_datetime),mssqldt.);
%let sql_end = %sysfunc(intnx(minute,&my_datetime,1),mssqldt.);
您可以看到要计算sql_start
我在sum()
中使用了%sysfunc()
函数并传入了SAS datetime变量。我这样做是因为它没有改变日期时间的值,并且允许我使用%sysfunc()
的第二个参数,它将指定的格式应用于返回的值。
对于sql_end
我按照惯例使用intnx()
函数,并再次使用第二个%sysfunc()
参数对其进行格式化。
让我们打印出值,看看它们的外观:
%put &sql_start &sql_end;
给出:
'2015-09-21 15:04:16.000' '2015-09-21 15:05:00'
然后它只是在你的代码中使用它的情况如下:
proc sql;
...
where tmstmp between &sql_start and &sql_end;
quit;
这里有一个地方的所有代码(假设您已经定义了格式):
%let my_datetime = %sysfunc(datetime());
%let sql_start = %sysfunc(sum(&my_datetime),mssqldt.);
%let sql_end = %sysfunc(intnx(minute,&my_datetime,1),mysqldt.);
%put &sql_start &sql_end;
proc sql;
...
where tmstmp between &sql_start and &sql_end;
quit;
现在,如果你想一次一个地提取数据,你可以把它全部编译成这样的循环:
%macro get_data(iStart=,iEnd=);
%local tmp_start tmp_end sql_start sql_end;
%let tmp_start = &iStart;
%do %while(&tmp_start le &iEnd);
%let tmp_end = %sysfunc(intnx(hour,&tmp_start,0,end));
/* MAKE SURE END OF LOOP ISNT GREATER THAN END DATETIME */
%if &tmp_end > &iEnd %then %do;
%let tmp_end = &iEnd;
%end;
%let sql_start = %sysfunc(sum(&tmp_start),mssqldt.);
%let sql_end = %sysfunc(sum(&tmp_end ),mssqldt.);
/* DO SQL HERE */
%put &sql_start &sql_end;
/* INCREMENT THE LOOP */
%let tmp_start = %sysfunc(intnx(hour,&tmp_start,1,beginning));
%end;
%mend;
今天打电话给明天的某个时间:
%get_data(iStart=%sysfunc(datetime()),
iEnd =%sysfunc(dhms(%sysfunc(date())+1,2,30,13))
);
生成的运行适用于以下时段:
'2015-09-21 15:25:33.000' '2015-09-21 15:59:59.000'
'2015-09-21 16:00:00.000' '2015-09-21 16:59:59.000'
'2015-09-21 17:00:00.000' '2015-09-21 17:59:59.000'
'2015-09-21 18:00:00.000' '2015-09-21 18:59:59.000'
'2015-09-21 19:00:00.000' '2015-09-21 19:59:59.000'
'2015-09-21 20:00:00.000' '2015-09-21 20:59:59.000'
'2015-09-21 21:00:00.000' '2015-09-21 21:59:59.000'
'2015-09-21 22:00:00.000' '2015-09-21 22:59:59.000'
'2015-09-21 23:00:00.000' '2015-09-21 23:59:59.000'
'2015-09-22 00:00:00.000' '2015-09-22 00:59:59.000'
'2015-09-22 01:00:00.000' '2015-09-22 01:59:59.000'
'2015-09-22 02:00:00.000' '2015-09-22 02:30:13.000'
答案 1 :(得分:0)
SAS将日期时间存储为秒数,因此您可以尝试添加& i minutes * 60seconds / minute来获得有效间隔,而不是添加一分钟。
where tmstmp between "&tmstmp_begin"dt + &i*60 and "&tmstmp_begin"dt + (&i+1)*60;
编辑:如果你有一个你想要的角色变量,但是只有当你使用实际的SAS日期时间值时才会工作。