【问题标题】:Create several SAS macro variable lists from single dataset从单个数据集创建多个 SAS 宏变量列表
【发布时间】:2019-03-17 06:28:27
【问题描述】:

由于宏变量值的长度不能超过 (65534) 的最大长度,因此我无法为所有观察结果创建单个宏变量。我想创建一个宏来遍历我的数据集以生成几个 numeric 列表,我可以将这些列表传递给 proc sql 中的 where 语句。

而不是这个:

*proc sql noprint;
    select ID into :ParaList separated by ','
    from work.ID_LIST(**firstobs=1 obs=5000**);
quit;*

*proc sql noprint;
    select ID into :ParaList2 separated by ','
    from work.ID_LIST(**firstobs=5001 obs=10000**);
quit;*

*proc sql noprint;
    select ID into :ParaList3 separated by ','
    from work.ID_LIST(**firstobs=10001 obs=15000**);
quit;*

*proc sql noprint;
    select ID into :ParaList4 separated by ','
    from work.ID_LIST(**firstobs=15001 obs=20000**);
quit;*

我想要类似的东西:

*proc sql noprint;
    select ID into :List1-Last4 separated by ','
    from work.ID_LIST(**firstobs=1 obs=&LASTOBS** BY 5000);
quit;*

我想创建一个宏来循环遍历每 5000 次左右的观察,直到最后一次观察我可以传递到 where 语句中,例如 where id in (&ParaList,&ParaList2,&ParaList3,&ParaList4)。我知道有其他选择,例如

id in (select id from work.table)

但在这种情况下,它不起作用。我正在通过 SAS 查询 Hadoop,除了传递宏变量列表之外没有任何成功。

【问题讨论】:

  • 对于稍微不同的方法,请查看此答案中的 %ds2list 宏:stackoverflow.com/questions/49899251/…。它将列表作为输出流式传输,因此它不会受到宏变量的相同限制。
  • 关于id in (select id from work.table),您可以使用SASTRACE 选项来记录SAS/Access 库引擎正在执行的较低级别的活动吗?

标签: loops sas sas-macro proc-sql


【解决方案1】:

你能在 Hadoop 中创建临时表吗?将您的 ID_LIST 数据上传到临时表,然后在传递查询中使用它。

libname hdp      hadoop noprompt="… connection parameters …";
libname hdp_temp hadoop noprompt="… connection parameters …" dbmstemp=yes;

proc delete data=hdp_temp.id_list;run;
data hdp_temp.id_list;
  set work.my_id_list;
run;

* im not sure on the Hadoop side object naming patterns and default schemas, 
* so this code shows dbo. as is the case in SQL Server;
* SAS libname option dmbstemp=yes for SQL Server causes created tables to be
* referenced as dbo.##<tablename>;

proc sql;
  connect using hadoop;
  create table work.susbset_of_big as
  select * from connection to Hadoop
  ( select * from dbo.my_big_remote_table
    where id in (select id from dbo.##id_list)
  );
quit;

【讨论】:

  • 我知道这是最好的方法,但我没有创建临时表的写入权限。感谢您的建议!
  • 每个人通常都有权创建它们。它们就像 SAS 中的工作台
  • 最佳实践是与您的 DBA 合作以获得编写适当解决方案所需的权限。
【解决方案2】:

您可以轻松地使用数据步骤来生成宏变量。您还应该生成一个调用所有其他宏变量的宏变量。

%let n_per_list=5 ;
data _null_;
  length idlist $32000;
  length macrolist $1000 ;
  retain macrolist;
  do i=1 to &n_per_list until (eof);
    set id_list end=eof;
    idlist=catx(',',idlist,id);
  end;
  listno+1;
  call symputx(cats('paralist',listno),idlist);
  macrolist=catx(',',macrolist,cats('&','paralist',listno));
  call symputx('paralist',macrolist);
run;

对于将 20 个值分成 5 个一组的简单测试会产生以下结果:

151  %put Paralist=%superq(ParaList);
Paralist=&paralist1,&paralist2,&paralist3,&paralist4
152  %put &=Paralist1;
PARALIST1=1,2,3,4,5
153  %put &=Paralist;
PARALIST=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20

或者您可以只考虑将代码生成为宏,而不是使用宏变量。这不应该对您可以生成多长时间的列表有任何限制。您可以尝试弄清楚如何在宏内部打开数据集而不生成任何 SAS 代码,以便宏调用的结果只是值列表。但是将宏定义的源代码生成到文件中然后 %include 文件来定义它会容易得多。

filename code temp;
data _null_;
  set id_list end=eof;
  file code lrecl=80;
  if _n_=1 then put '%macro paralist;' ;
  else put ',' @ ;
  put id @ ;
  if eof then put / '%mend paralist;' ;
run;

同样平凡的 20 值列表的结果。

163  %include code / source2;
NOTE: %INCLUDE (level 1) file CODE is file /.../#LN00043.
164 +%macro paralist;
165 +1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20
166 +%mend paralist;
NOTE: %INCLUDE (level 1) ending.
167  %put %paralist;
1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 ,9 ,10 ,11 ,12 ,13 ,14 ,15 ,16 ,17 ,18 ,19 ,20

【讨论】:

  • 谢谢汤姆,您提供的第一个代码运行良好。我只需要进行调整,因为某些 id 值的长度为 9 或更大,这截断了我生成的一些列表。我只是缩短了 n_per_list 即 5000 到 3000 以避免这种情况。我需要更多时间来处理您提供的第二个解决方案,如果我有任何问题,请告诉您。再次感谢,这很有帮助!
【解决方案3】:

我喜欢@Richard 解决方案,它会让它变得更干净、更高效。如果您的数据集不是特别大,您也可以尝试隐式传递。

 Libname SASTAB "yoursaslocation";
 Libname HTAB  your hadoop parameters;

 proc sql;
 create HTAB.newtable as 
 select * froom HTAB.yourtable
 where id in (Select id from SASTAB.yoursastable);

【讨论】:

  • 是的,就像我在帖子中分享的那样,这种方法没有成功。出于某种原因,当我尝试隐式传递时,我不断收到错误,或者它只是冻结了。
【解决方案4】:

您可以使用data step do 循环和call execute() 来执行此操作。

data _null_;
set lib.id_list (nobs=totobs);

do i = 1 to totobs by 5000;

call execute(cat(
    'proc sql;
     select ID into :paralist', i,' separated by ','
     from lib.id_list (firstobs=', i,
    ' obs=5000); quit;'
    ));

call execute(cats('%put |paralist',i,'|;'));

end;
run;

阅读更多关于execute函数调用data stepREF1REF2

上面的调用生成了一行

proc sql;
select id into:paralist1 separated by ','
from lib.id_list (firstobs=1 obs=5000);
quit;

proc sql;
select id into:paralist5001 separated by ','
from lib.id_list (firstobs=5001 obs=5000);
quit;

/* etc */

【讨论】:

  • 我一直收到 put cat('|paralist',i,'|') 的错误,说明:错误 22-322:语法错误,需要以下之一:名称、数组名、ALL、CHARACTERCHARNUMERIC.
  • 尝试删除该行并运行该行。发布现在出现的错误。我目前无法访问 SAS,但如果您在删除该行后发布错误,可以提供帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多