【问题标题】:SAS Adding Minutes to a Timestamp Stored in a Macro VariableSAS 将分钟添加到存储在宏变量中的时间戳
【发布时间】:2015-09-21 21:57:55
【问题描述】:

我正在通过 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'

谢谢!

【问题讨论】:

    标签: sql sql-server sas sas-macro


    【解决方案1】:

    我发现处理这些情况的最佳方法是使用自定义日期时间格式。您可以找到有关构建它们的链接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 格式化变量提供单独的变量。例如

    计算我们想要使用的日期时间:

    %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,我使用了%sysfunc() 中的sum() 函数并传入了SAS 日期时间变量。我这样做是因为它不会更改日期时间的值,并且允许我使用 %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'
    

    【讨论】:

    • 罗伯特的回答很棒——正是我想要的。感谢您抽出宝贵时间将其写得如此详细。
    【解决方案2】:

    SAS 将日期时间存储为秒数,因此您可以尝试添加 &i 分钟*60 秒/分钟以获得有效间隔,而不是添加一分钟。

    where tmstmp between "&tmstmp_begin"dt + &i*60 and "&tmstmp_begin"dt + (&i+1)*60;
    

    编辑:如果您有一个看起来像的字符变量,这将不起作用,但前提是您使用的是实际的 SAS 日期时间值。

    【讨论】:

    • 感谢 Reeza 的回答,但我的问题是我无法添加到 &tmstmp_begin(有或没有分钟);我认为是因为它作为字符串存储在 sql 表中。
    • 通常日期时间戳是日期时间,但宏变量是字符,我稍微修改了答案,但@Robert 的答案可能仍然更可取。我认为上述方法也应该有效。
    猜你喜欢
    • 2013-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-22
    • 1970-01-01
    • 1970-01-01
    • 2016-09-04
    • 1970-01-01
    相关资源
    最近更新 更多