如何以 YYYYMM 格式编写几个月的宏循环?

How to write a macro loop over months in YYYYMM format?

我的 SAS 工作文件夹中有 26 个表,它们采用 yearMONTH 格式,从 201301 到 201502(即 201301 到 201312、201401 到 201412、201501、201502)。我必须创建 26 个新表,并加入它们(有关更多详细信息,请参见下面的示例代码)。

我应该如何循环数据?我打算使用宏,但不太确定如何循环播放它。另外,我相信有比使用宏更好的方法。

%Macro YearMonth(YM=  );

PROC SQL;
   CREATE TABLE WORK.Join&YM AS 
   SELECT t1.ID, 
      t1.Eff_YM, 
      t1.Trm_YM 

  FROM WORK.DATASET_&YM AS t1 
        LEFT JOIN Work.Names AS t2 
            AND ( input(t1.YearMonth, 6.) = t2.Paid_End_dt2j);
QUIT;
%mend;
%YearMonth(YM = 201301) 
....
%YearMonth(YM = 201502) 

在评论中,vol7ron 提出了一个重要的问题:

Why do you have 26 tables?? How many observations in your dataset? Can you not append into one dataset?

每个月都有一个单独的数据集表明上游设计有问题。但是,让我们假设这是您无法控制的。通过宏循环迭代月份的一种非常简单的方法如下:

%macro loop(start_month=, stop_month=);
    %local month;
    %do month=&start_month %to &stop_month;
        %put Month: &month;

        %* SQL CODE HERE....

        %* SPECIAL CASE WHEN WE REACH END OF A YEAR;
        %if %substr(&month, 5, 2) = 12 %then %let month = %eval(&month + 88);       
    %end;
%mend loop;

%loop(start_month=200301, stop_month=201502)

注意 SQL 代码的占位符。然而,这个基本模板适用于任何类似的情况。关键是 %end 语句之前的最后一行,它将循环变量递增 88,使其从(例如)201312 变为 201400。然后,控制到达 %do 循环的顶部,其中 &month 增加到 201401。因此,如果在某些迭代中 &month 等于 201312,那么在下一次迭代中它将等于 201401。

有几点不同于 Matthew 总体上合理的回答。

首先,我强烈建议将您的 sql 代码编写在与执行循环的宏不同的宏中(如果您为此使用了宏)。它使测试更容易,修改更容易,并且通常是良好的编程习惯。因此,如果你走宏循环的路线,Matthew 有 %* PUT SQL CODE HERE;,实际上会调用你已经编写的另一个宏。

其次,虽然 macro-loop 方法很好而且有时是正确的方法,但更容易维护的方法是从数据而不是宏循环中调用宏。这就是我 95% 的代码对 probal 所做的事情。

这在以下情况下会明显优越:

  1. 您已经有一个数据集,其中 26 个月存储为一个变量(26​​ 个观察值,每月一个)。不一定主要是为了这个目的——只要它已经存在于某处即可。
  2. 您已经在某处的 libname 中拥有了这 26 个数据集(而不是您不想要的其他数据集),其中它们要么是唯一的数据集,要么是唯一像它们一样命名的数据集(更常见)。

它也可以用在其他情况下——像上面1那样制作数据集很容易,比宏循环更容易一些——但在那些情况下不一定添加。

假设您有 2) - 您在 WORK 库中有 DATASET_201301DATASET_201412,没有其他标题为 DATASET_。然后你可以像这样使用dictionary.tables

proc sql;
  select cats('%YearMonth(ym=',scan(memname,2,'_'),')')
    into :callyears separated by ' '
    from dictionary.tables
    where libname='WORK' and memname like 'DATASET_%'
  ;
quit;
&callyears.

这会创建一个宏变量 &callyears.,它存储来自 select 的文本 - 在本例中,我们使用连接函数 cats 创建的宏调用。 memname是数据集名,libname当然是libname。

这是使用 data-driven 方法调用宏的相当简单的方法;这意味着下次您需要 运行 时,您不必在此处更改代码中的任何内容 - 只要在此之前创建 26 个数据集的代码提取 26 个新数据集(或任何数量) !), 你可以开始了。如果你添加第 27 或第 28 个,它也会自动拉出那些。当然,您可以在 where 语句中包含一些内容以过滤掉内容 - 例如 where libname='WORK' and memname like 'DATASET_%' and scan(memname,2,'_') ge '201301'