【问题标题】:SAS pass-through facility. How to insert a big list from a local table in a query?SAS 直通设施。如何在查询中从本地表中插入一个大列表?
【发布时间】:2019-04-01 19:59:53
【问题描述】:

我需要使用 SAS 直通工具查询服务器 (REMOTE_TBL) 中的一个大表。为了使查询更短,我想发送从本地表 (LOCAL_TBL) 中提取的 ID 列表。 我的第一步是使用 INTO 语句将 ID 放入名为 id_list 的变量中:

select distinct ID into: id_list separated by ',' from WORK.LOCAL_TBL 

然后我将这些 ID 传递给传递查询:

PROC SQL;
CONNECT TO sybaseiq AS dbcon
(host="name.cl" server=alias db=iws user=sas_user password=XXXXXX);

create table WANT as 
select * from connection to dbcon(
  select * 
  from    dbo.REMOTE_TBL
  where   ID in (&id_list)
);
QUIT;

代码运行良好,但我收到以下消息:

The length of the value of the macro variable exceeds the maximum length

有没有更简单的方法可以将选定的 ID 发送到直通查询? 有没有办法将选定的 ID 存储在两个或多个变量中?

【问题讨论】:

  • 您可能必须将其分解为单独的宏变量。
  • 关于那些与 REMOTE_TBL 相关的子集 ID 的任何信息?它们是来自那个表还是另一个表。大的IN() 可能存在性能问题。

标签: sas sas-macro proc-sql pass-through


【解决方案1】:

将值存储到多个宏变量中,然后将宏变量的名称存储到另一个宏变量中。

所以这段代码会生成一系列宏变量,分别命名为 M1, M2, .... 然后将 ID_LIST 设置为 &M1,&M2....

data _null_;
length list $20200 mlist $20000;
do until(eof or length(list)>20000);
  set LOCAL_TBL end=eof;
  list=catx(',',list,id);
end;
call symputx(cats('m',_n_),list);
mlist=catx(',',mlist,cats('&m',_n_));
if eof then call symputx('id_list',mlist);
run;

然后,当您展开 ID_LIST 时,宏处理器将展开所有单独的 Mx 宏变量。这个小数据步骤将创建几个示例宏变量来演示这个想法。

data _null_;
  call symputx('m1','a,b,c');
  call symputx('m2','d,e,f');
  call symputx('id_list','&m1,&m2');
run;

结果:

70    %put ID_LIST=%superq(id_list);
ID_LIST=&m1,&m2
71    %put ID_LIST=&id_list;
ID_LIST=a,b,c,d,e,f

【讨论】:

    【解决方案2】:

    您正在传递许多出现在IN (…) 子句中的数据值。允许的值的数量因数据库而异;有些可能限制为每个子句 250 个值,并且语句的长度可能有限制。如果宏变量创建了一个 20,000 个字符长的值列表,那么远程端可能不会这样。

    在处理可能 > 100 个值的查找时,请先花一些时间将您的需求传达给数据库管理员以创建临时表。当你拥有这样的权限时,你的远程端查询会更有效率。

    … upload id values to #myidlist … 
    create table WANT as 
    select * from connection to dbcon(
      select * 
      from    dbo.REMOTE_TBL
      where   ID in (select id from #myidlist)
    );
    QUIT;
    

    如果您无法获得适当的权限,则必须将 id 列表切碎并让宏创建一系列 ORed IN 搜索。

    1=0
    OR ID IN ( … list-values-1 … )
    … 
    OR ID IN ( … list-values-N … )
    

    例如:

    data have;
      do id = 1 to 44;
        output;
      end;
    run;
    
    %let IDS_PER_MACVAR = 10;  * <---------- make as large as you want until error happens again;
    
    * populated the macro vars holding the chopped up ID list;
    data _null_;
      length macvar $20;    retain macvar;
      length macval $32000; retain macval;
      set have end=end;
    
      if mod(_n_-1, &IDS_PER_MACVAR) = 0 then do;
    
        if not missing(macval) then call symput(macvar, trim(macval));
        call symputx ('VARCOUNT', group);
    
        group + 1;
        macvar = cats('idlist',group);
        macval = '';
      end;
    
      macval = catx(',',macval,id);
    
      if end then do;
        if not missing(macval) then call symput(macvar, trim(macval));
        call symputx ('MVARCOUNT', group);
      end;
    run;
    
    * macro that assembles the chopped up bits as a series of ORd INs;
    %macro id_in_ors (N=,NAME=);
       %local i;
    
    1 = 0
    
       %do i = 1 %to &N;
    OR ID IN (&&&NAME.&i)
       %end;
    
    %mend;
    
    * use %put to get a sneak peek at what will be passed through;
    %put %id_in_ors(N=&MVARCOUNT,NAME=IDLIST);
    
    
    * actual sql with pass through;
    ...
    
    create table WANT as 
    select * from connection to dbcon(
      select * 
      from    dbo.REMOTE_TBL
      where   ( %ID_IN_ORS(N=&MVARCOUNT,NAME=IDLIST) )  %* <--- idlist piecewise ors ;
    );
    
    ...    
    

    【讨论】:

      【解决方案3】:

      我建议您首先将所有不同的值保存到一个表中,然后(再次使用proc sql + into)将这些值加载到几个独立的宏变量中,在几组中多次读取该表;事实上,它们必须是相互排斥的,但又是详尽无遗的。

      您是否有权访问并在您的dbo.REMOTE_TBL 所在的数据库中创建权限?如果是这样,您可能还考虑将您的WORK.LOCAL_TBL 复制到数据库中的临时表中并在此处运行内部连接。

      【讨论】:

      • 我对远程数据库只有“只读”权限。我暂时会听从你的建议,但我希望以前有人解决过这个问题。谢谢!
      • 独立于我,@RIchard 提出了与上述相同的方法:与您的数据库管理员交谈,让您有权创建(临时)表。我的经验:它可能会一次又一次地交出。
      【解决方案4】:

      另一个选项 - 将查询写入临时文件,然后 %include 它。无需宏逻辑!

      proc sort 
        data = WORK.LOCAL_TBL(keep = ID) 
        out = distinct_ids 
        nodupkey;
      run;
      
      data _null_;
        set distinct_ids end = eof;
        file "%sysfunc(pathname(work))/temp.sas";
        if _n_ = 1 then put "PROC SQL;
          CONNECT TO sybaseiq AS dbcon
          (host=""name.cl"" server=alias db=iws user=sas_user password=XXXXXX);
          create table WANT as 
            select * from connection to dbcon(
              select * 
                from    dbo.REMOTE_TBL
                where   ID in (" @;
        put ID @;
        if not(eof) then put "," @;
        if eof then put ");QUIT;" @;
        put;
      run;
      
      /*Use nosource2 to avoid cluttering the log*/
      %include "%sysfunc(pathname(work))/temp.sas" /nosource2; 
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-21
        • 2013-11-15
        • 2018-05-05
        • 1970-01-01
        • 2013-08-16
        相关资源
        最近更新 更多