3

更新

鉴于使用这种新方法,INTNX我认为我可以使用循环来进一步简化事情。如果我做了一个数组怎么办:

data;
    array period [4] $ var1-var4 ('day' 'week' 'month' 'year');
run;

然后尝试为每个元素创建一个循环:

%MACRO sqlloop;
  proc sql;
    %DO k = 1 %TO dim(period);  /* in case i decide to drop something from array later */
      %LET bucket = &period(k)
      CREATE TABLE output.t_&bucket AS (
        SELECT INTX( "&bucket.", date_field, O, 'E') AS test FROM table);
    %END
  quit;
%MEND
%sqlloop

这不太有效,但它抓住了我想要的想法。它可以只为 INTX 中的每个值运行查询。那有意义吗?


我有几个先前的问题要合并为一个。我得到了一些关于其他人的非常有用的建议,希望这可以将它们联系在一起。

我有以下函数创建一个动态字符串来填充SELECTSASproc sql;代码块中的语句:

proc fcmp outlib = output.funcs.test;
    function sqlSelectByDateRange(interval $, date_field $) $;
        day = date_field||" AS day, ";
        week = "WEEK("||date_field||") AS week, ";
        month = "MONTH("||date_field||") AS month, ";
        year = "YEAR("||date_field||") AS year, ";

        IF interval = "week" THEN
            do;
                day = '';
            end;
        IF interval = "month" THEN
            do;
                day = '';
                week = '';
            end;
        IF interval = "year" THEN
            do;
                day = '';
                week = '';
                month = '';
            end;
        where_string = day||week||month||year;
    return(where_string);
    endsub;
quit;

我已经验证这会创建我想要的那种字符串:

data _null_;
    q = sqlSelectByDateRange('month', 'myDateColumn');
    put q =;
run;

这产生:

q=MONTH(myDateColumn) AS month, YEAR(myDateColumn) AS year,

这正是我想要的 SQL 字符串。从之前的问题来看,我相信我需要在MACRO. 然后我想要这样的东西:

%MACRO sqlSelectByDateRange(interval, date_field);
  /* Code I can't figure out */
%MEND

PROC SQL;
  CREATE TABLE output.t AS (
    SELECT 
      %sqlSelectByDateRange('month', 'myDateColumn')
    FROM
      output.myTable
  );
QUIT;

我无法理解如何使代码调用此宏并将其解释为 SQL SELECT 字符串的一部分。我已经在其他答案中尝试了一些前面的例子,但我无法让它工作。我希望这个更具体的问题可以帮助我填补这个缺失的步骤,这样我就可以在未来学习如何去做。

4

3 回答 3

3

两件事情:

首先,您应该能够使用%SYSFUNC来调用您的自定义函数。

%MACRO sqlSelectByDateRange(interval, date_field);
    %SYSFUNC( sqlSelectByDateRange(&interval., &date_field.) )
%MEND;

请注意,通过 SYSFUNC 调用函数时不应使用引号。此外,在 SAS 9.2 之前,您不能将 SYSFUNC 与 FCMP 函数一起使用。如果您使用的是早期版本,这将不起作用。

其次,您的 select 子句中有一个尾随逗号。您可能需要一个虚拟列,如下所示:

PROC SQL;
  CREATE TABLE output.t AS (
    SELECT 
      %sqlSelectByDateRange('month', 'myDateColumn')
      0 AS dummy
    FROM
      output.myTable
  );
QUIT;

(请注意,之前没有逗号dummy,因为逗号已经嵌入到您的宏中。)


更新

我阅读了您对另一个答案的评论:

我还需要能够在不同的日期范围和非常临时的基础上做到这一点,所以当有人制作一个要求。

我想我可以推荐一种更简单的方法来完成你正在做的事情。首先,我将创建一个包含日期和值的非常简单的数据集。这些日期分布在不同的天、周、月和年:

DATA Work.Accounts;

    Format      Opened      yymmdd10.
                Value       dollar14.2
                ;

    INPUT       Opened      yymmdd10.
                Value       dollar14.2
                ;

DATALINES;
2012-12-31  $90,000.00
2013-01-01 $100,000.00
2013-01-02 $200,000.00
2013-01-03 $150,000.00
2013-01-15 $250,000.00
2013-02-10 $120,000.00
2013-02-14 $230,000.00
2013-03-01 $900,000.00
RUN;

您现在可以使用该INTNX函数创建第三列以将“已打开”列四舍五入到某个时间段,例如 a 'WEEK''MONTH''YEAR'(请参阅此完整列表):

%LET Period = YEAR;

PROC SQL NOPRINT;

    CREATE TABLE Work.PeriodSummary AS
    SELECT   INTNX( "&Period.", Opened, 0, 'E' ) AS Period_End FORMAT=yymmdd10.
           , SUM( Value )                        AS TotalValue FORMAT=dollar14.
    FROM     Work.Accounts
    GROUP BY Period_End
    ;

QUIT;

输出WEEK

Period_End   TotalValue
2013-01-05     $540,000
2013-01-19     $250,000
2013-02-16     $350,000
2013-03-02     $900,000

输出MONTH

Period_End   TotalValue
2012-12-31      $90,000
2013-01-31     $700,000
2013-02-28     $350,000
2013-03-31     $900,000

输出YEAR

Period_End   TotalValue
2012-12-31      $90,000
2013-12-31   $1,950,000
于 2013-07-15T16:24:40.693 回答
2

正如 Cyborg37 所说,您可能应该摆脱函数中的尾随逗号。但请注意,您实际上并不需要创建宏来执行此操作,只需%SYSFUNC直接使用该函数即可:

proc sql;
  create table output.t as
  select %sysfunc( sqlSelectByDateRange(month, myDateColumn) )
         * /* to avoid the trailing comma */
  from output.myTable;
quit;

此外,虽然这是对用户定义函数的巧妙使用,但不清楚您为什么要这样做。可能有更好的解决方案不会在您的代码中造成太多潜在的混乱。用户定义的函数,如用户编写的宏,可以让生活更轻松,但它们也可能造成管理上的噩梦。

于 2013-07-15T16:39:42.400 回答
1

我可以对你为什么会出错做出各种各样的猜测,但从根本上说,不要这样做。您可以在数据步骤中执行您想要做的事情,这比 FCMP 功能更容易排除故障并且更容易实现,而 FCMP 功能实际上只是试图成为一个数据步骤。

步骤: 1. 创建一个包含您可能的日期拉取数据的数据集。如果您经常使用它,您可以将它放在您的 SAS AUTOEXEC 中定义的永久库中。2. 创建一个宏,从中提取所需的日期字符串。3. 如果需要,可以使用 PROC FCMP 将其设为函数式宏,使用 RUN_MACRO。4. 如果你这样做,使用 %SYSFUNC 调用它。

这是这样做的:

1:

data pull_list;
infile datalines dlm='|';
length query $50. type $8.;
input type $ typenum query $;
datalines;
day|1|&date_field. as day
week|2|week(&date_field.) as week
month|3|month(&date_field.) as month
year|4|year(&date_field.) as year
;;;;
run;

2:

%macro pull_list(type=,date_field=);
%let date_field = datevar;
%let type = week;
proc sql noprint;
select query into :sellist separated by ',' 
from pull_list
where typenum >= (select typenum from pull_list where type="&type.");
quit;
%mend pull_list;

3:

proc fcmp outlib = work.functions.funcs;
   function pull_list(type $,date_field $) $;
      rc = run_macro('pull_list', type,date_field);
      if rc eq 0 then return("&sellist.");
      else return(' ');
   endsub;
run;

4:

data test;
input datevar 5.;
datalines;
18963
19632
18131
19105
;;;;
run;
option cmplib = (work.functions);

proc sql;
select %sysfunc(pull_list(week,datevar)) from test;
quit;

这样做的一大优点是您可以添加其他类型而不必担心函数的代码 - 只需向 pull_list 添加一行即可。如果你想设置它来做到这一点,我建议对 typenum 使用 1,2,3,4 以外的东西 - 使用 10,20,30,40 或其他东西,这样你就有差距(比如,如果添加了“twoweek” ,它会在 2 到 3 之间,而 25 比 2.5 更容易让人思考)。创建该 pull_list 数据集,将其放在所有用户都可以使用的网络驱动器上(如果您以外的任何人使用它,或者如果没有,则使用个人),然后从那里开始。

于 2013-07-15T17:27:44.487 回答