SAS / SQL - 使用自定义函数创建SELECT语句

时间:2013-07-15 16:14:19

标签: sql sas

更新

鉴于这种使用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中的每个值运行查询。这有意义吗?


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

我有以下函数创建一个动态字符串来填充SAS SELECT代码块中的proc 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字符串的一部分。我在其他答案中尝试了一些前面的例子,但我无法使其工作。我希望这个更具体的问题可以帮助我填补这个缺失的步骤,以便我将来可以学习如何做到这一点。

3 个答案:

答案 0 :(得分:3)

两件事:

首先,您应该可以使用%SYSFUNC来调用自定义函数。

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

请注意,在通过SYSFUNC调用函数时不应使用引号。另外,you cannot use SYSFUNC with FCMP functions until SAS 9.2。如果您使用的是早期版本,则无法使用。

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

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

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


<强>更新

我在另一个答案中读到你的评论:

  

我还需要能够针对不同的日期范围和非常特别的方式来做这件事,所以我想说“从6月到12月的月份”或“每周两周”等等。有人提出要求。

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

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功能创建第三列,将“已打开”列舍入到某个时间段,例如'WEEK''MONTH'或{{1 (见complete list):

'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

答案 1 :(得分:2)

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

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

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

答案 2 :(得分: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中添加一行即可。如果你想设置它,我建议使用1,2,3,4以外的东西来使用typenum - 使用10,20,30,40或者其他东西,这样你就有空隙(比方说,如果&#34; twoweek& #34;被添加,它将介于2和3之间,25比人们更容易思考25。创建pull_list数据集,将其放在网络驱动器上,您的所有用户都可以使用它(如果您之外的任何人使用它,或者如果没有,则使用个人),然后从那里开始。