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

SAS/SQL-使用自定义函数创建SELECT语句,sql,sas,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

更新

考虑到这种使用
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
procsql中的
SELECT
语句代码块:

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 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选择字符串的一部分。我已经在其他答案中尝试了前面的一些例子,但我就是不能让它起作用。我希望这个更具体的问题能帮助我填补这一缺失的步骤,以便我将来能学会如何做。

两件事:

首先,您应该能够使用调用自定义函数

%MACRO sqlSelectByDateRange(interval, date_field);
    %SYSFUNC( sqlSelectByDateRange(&interval., &date_field.) )
%MEND;
请注意,通过SYSFUNC调用函数时不应使用引号。也。如果您使用的是早期版本,这将不起作用

其次,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
函数创建第三列,将“已打开”列四舍五入到某个时间段,例如
“周”
“月”
,或
“年”
(请参见此):

周的输出

Period\u End TotalValue
2013-01-05     $540,000
2013-01-19     $250,000
2013-02-16     $350,000
2013-03-02     $900,000
月份的输出

Period\u End TotalValue
2012-12-31      $90,000
2013-01-31     $700,000
2013-02-28     $350,000
2013-03-31     $900,000
年度的输出

Period\u End TotalValue
2012-12-31      $90,000
2013-12-31   $1,950,000

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

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

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

我可以对出现错误的原因进行各种猜测,但基本上,不要这样做。与FCMP函数相比,数据步骤更容易排除故障,也更容易实现。FCMP函数实际上只是一个数据步骤

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

这里有一些东西可以做到这一点:

1:

2:

3:

4:


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

是的,这很有意义。不过,我在长度方面也遇到了同样的错误。我不知道那是怎么回事。至于为什么,我很想听听你的想法。我将运行一些需要按时间范围(天、周、月、年等)分组的报告。理想情况下,我更愿意按天运行数据集,并按照我的意愿对其进行聚合。不幸的是,我查询的一个主要领域是不同的个人,他们可能会在数周或数月内有所不同。考虑到数据的大小,在导出摘要之前,我希望在给定的级别上执行不同的计数。我还需要能够针对不同的日期范围和非常特殊的基础进行计数,因此我想说的是“从6月到12月按月”或“两年内每周”当有人提出请求时,@JeffreyKramer-我可能有一个更简单的方法让你实现你的目标。看看上面,你没有在你的函数中定义事物的长度,所以你冒着事情变得非常大的风险——也许所有这些都发生了
proc sql;
  create table output.t as
  select %sysfunc( sqlSelectByDateRange(month, myDateColumn) )
         * /* to avoid the trailing comma */
  from output.myTable;
quit;
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;
%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;
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;
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;