首页 > 解决方案 > SAS Macro in macro - 如何改变流程?

问题描述

你写道我不应该在 %macro 中设置 %macro。所以请帮助我的过程。创世纪:我有一张表,其中存储了每天开始的所有 SAS 流程 - 但各种报告应该在不同的日子运行 - 根据字段“Nr_week_day”。如果今天包含在这个字段中——我把这个过程放到general_stock 表中运行。下面是我的评论代码 - 我想要得到什么。通常 - 此代码有效,但它与宏中的宏一起使用。也许你有更好的想法什么可以工作。这个过程与我的另一个“问题”有关: SAS include dynamic path SAS Macro in macro

    data CONTROL_FILES_BASE;
       input  Priority : 2.
              ACTIVE: 1.
              PROCES_NAME: $10.
              Nr_week_day: $10.

    ;   cards;
    1 1 TEST_01 (1,3,6)
    2 1 TEST_02 0
    3 1 TEST_03 (4,5)
    ;

    Data Kalendariusz;
    infile cards dlm=',' dsd;
       input  ref_date: date9.
              Nr_day_of_month
              Nr_week_day
              Number_date;
        Format ref_date DDMMYY10.;
    cards;
    01NOV2018,  1,  4,  20181101
    02NOV2018,  2,  5,  20181102
    03NOV2018,  3,  6,  20181103
    04NOV2018,  4,  7,  20181104
    05NOV2018,  5,  1,  20181105
    06NOV2018,  6,  2,  20181106
    07NOV2018,  7,  3,  20181107
    08NOV2018,  8,  4,  20181108
    09NOV2018,  9,  5,  20181109
    10NOV2018,  10, 6,  20181110
    11NOV2018,  11, 7,  20181111
    12NOV2018,  12, 1,  20181112
    13NOV2018,  13, 2,  20181113
    14NOV2018,  14, 3,  20181114
    15NOV2018,  15, 4,  20181115
    16NOV2018,  16, 5,  20181116
    17NOV2018,  17, 6,  20181117
    18NOV2018,  18, 7,  20181118
    19NOV2018,  19, 1,  20181119
    20NOV2018,  20, 2,  20181120
    21NOV2018,  21, 3,  20181121
    22NOV2018,  22, 4,  20181122
    23NOV2018,  23, 5,  20181123
    24NOV2018,  24, 6,  20181124
    25NOV2018,  25, 7,  20181125
    26NOV2018,  26, 1,  20181126
    27NOV2018,  27, 2,  20181127
    28NOV2018,  28, 3,  20181128
    29NOV2018,  29, 4,  20181129
    30NOV2018,  30, 5,  20181130
    ;

    /*COMMENT: I TAKE TODAY IN VARIABLE*/
    %LET EXTRACT_DATE_DT = date();

    /*COMMENT: I CREATE EMPTY TABLE TO STOCK OF PROCESS*/
    Proc sql;
    Create table GENERAL_STOCK as
    Select
    *
    FROM WORK.CONTROL_FILES_BASE
    WHERE ACTIVE = 2
    ;quit;

   /*START MAIN MACRO*/ 
    %macro GENERATE_STOCK();

    /*COMMENT: I check how many processes should be generated.*/
        PROC SQL noprint;
            Select
            count(*) into :i
            From work.CONTROL_FILES_BASE
            WHERE Nr_week_day <> '0'
        ;quit;
    %PUT &i;

    /*COMMENT: I separated process which should be check*/
    Proc sql;
    Create table STOCK_2 as
    Select
    monotonic() as ROW_ID,
    *
    FROM work.CONTROL_FILES_BASE
    WHERE Nr_week_day ne '0'
    ;quit;

    /*MAIN LOOP - I take field NR_WEEK_DAY and will check that this number of day is today - row by row*/
    %do ITER = 1 %To &i;

                Proc sql;
                Select
                    Nr_week_day into :SET_VAR
                    from STOCK_2
                    WHERE ROW_ID = &ITER
                ;quit;
            %PUT &SET_VAR;
    /*SET_VAR have value from Nr_week_day*/

        %LET l_decision = 0;/*I set default value in variable*/
    /*below code I found in forum - this macro reverse query - check whether (1,3,6) is included today - in table KALENDARIUSZ*/
            %macro nos_obs(dsn=,where_stmt=);
                proc sql;
                    select 
                    count(*) 
                    into :l_decision
                    from &dsn.
                    &where_stmt.
                ;quit;
            %mend ;
            %nos_obs(dsn=Kalendariusz,where_stmt=where Nr_week_day in &&SET_VAR. and Ref_date = &EXTRACT_DATE_DT.);
        %PUT &l_decision;

    /*IF ABOVE CODE RETURN 1 -  means that the nr_week_day is today */
        /*When l_decisions is 1 then process should add this row to general_stock. If 0 - should add nothing.*/
        %if &l_decision = 1 %then
            %do;
                Proc sql;
                Create table STOCK_2_INSERT (drop=ROW_ID) as
                Select
                *
                FROM WORK.STOCK_2
                WHERE ROW_ID = &ITER
                ;quit;

                Proc sql;
                    insert into GENERAL_STOCK
                    select * from work.STOCK_2_INSERT
                ;quit;

                /*I clear temp table*/
                Proc sql;
                    delete FROM WORK.STOCK_2_INSERT
                ;quit;
            %end;
        %else %if &l_decision = 0 %then
            %do;
            %end;
    %end;
    %mend GENERATE_STOCK;
    %GENERATE_STOCK();

/*AND I LOOK AT GENERAL TABLE*/
    Proc sql;
    Create table SHOW_GENERAL_STOCK as
    Select
    *
    FROM WORK.GENERAL_STOCK
    ;quit;

标签: sassas-macro

解决方案


正如您在其他问题的答案中所解释的那样,在另一个宏定义中定义一个宏是个坏主意。在您的示例中,这意味着您可以移动实用程序宏 %nos_obs 的定义:

%macro nos_obs(dsn=,where_stmt=);
  proc sql;
    select 
    count(*) 
    into :l_decision
    from &dsn.
    &where_stmt.
    ;quit;
%mend ;

该代码块不应在块内:

%macro GENERATE_STOCK;
   ...
%mend GENERATE_STOCK;

您仍然可以从 %generate_stock 中调用 %nos_obs。只是不要嵌套宏定义。所以你最终得到:

*define a macro;
%macro nos_obs(dsn=,where_stmt=);
...
%mend ;

*define a macro that does some stuff and invokes another macro;
%macro GENERATE_STOCK;
   ...
   %nos_obs(dsn=...)
   ...
%mend GENERATE_STOCK;

%generatestock

这是关于不嵌套宏定义的一般要点。从大局来看,您似乎正在用 SAS 编写调度程序。就像 linux cron 或 windows 调度程序,您可以根据星期几来决定运行哪些程序。 通常最好使用专用的调度程序解决方案(cron、LSF、Windows 调度程序等),而不是自己编写。 更好意味着更容易、更可维护、更灵活等。它们将让您管理依赖关系、暂停和重新启动等。

也就是说,如果您确实在 SAS 中编写了自己的调度程序(很多人都这样做,有时很难抗拒诱惑),我认为您展示的代码比它需要的复杂得多。

您有一个控制数据集,其中列出了每个进程应运行的日期:

data CONTROL_FILES_BASE;
   input  Priority : 2.
          ACTIVE: 1.
          PROCES_NAME: $10.
          Nr_week_day: $10.

;   cards;
1 1 TEST_01 (1,3,6)
2 1 TEST_02 0
3 1 TEST_03 (4,5)
;

如果您想确定今天应该运行哪些流程,您只需找出今天是星期几,并相应地选择记录。就像是:

data General_Stock ;
  set CONTROL_FILES_BASE ;
  where findc(Nr_week_day,put(weekday(today()),1.)) ;
run ;

当我写这篇文章时是星期六,所以 weekday(today()) 返回 7 并且上面选择了 0 条记录,因为没有安排在星期六运行的进程。

如果你想要一个宏,因为你想测试你的控制数据集会在不同的日期触发哪些进程,你可以写一个小宏来输入提取日期。就像是:

%macro GENERATE_STOCK
  (data=/*name of input control dataset*/
  ,out= /*name of output dataset*/
  ,ExtractDate=/*extract date is a SAS date or expression like today() */
  );

  data &out ;
    set &data ;
    where findc(Nr_week_day,put(weekday(&extractDate),1.)) ;
  run ;

  title1 "Printout of &out genenerated when ExtractDate=%superq(ExtractDate)" ;
    proc print data=&out ;
    run ;
  title1 ;

%mend GENERATE_STOCK ;

测试如下:

%generate_stock(data=control_files_base,out=wantToday    ,extractdate=today())
%generate_stock(data=control_files_base,out=wantSunday   ,extractdate="11Nov2018"d)
%generate_stock(data=control_files_base,out=wantMonday   ,extractdate="12Nov2018"d)
%generate_stock(data=control_files_base,out=wantTuesday  ,extractdate="13Nov2018"d)
%generate_stock(data=control_files_base,out=wantWednesday,extractdate="14Nov2018"d)
%generate_stock(data=control_files_base,out=wantThursday ,extractdate="15Nov2018"d)
%generate_stock(data=control_files_base,out=wantFriday   ,extractdate="16Nov2018"d)
%generate_stock(data=control_files_base,out=wantSaturday ,extractdate="17Nov2018"d)

推荐阅读