SAS сквозной объект.Как вставить большой список из локальной таблицы в запрос? - PullRequest
0 голосов
/ 01 апреля 2019

Мне нужно запросить большую таблицу на сервере (REMOTE_TBL), используя функцию сквозного доступа SAS. Чтобы сделать запрос короче, я хочу отправить список идентификаторов, извлеченных из локальной таблицы (LOCAL_TBL). Мой первый шаг - вставить идентификаторы в переменную с именем id_list, используя оператор INTO:

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

Затем я передаю эти идентификаторы в сквозной запрос:

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

Есть ли более простой способ отправить выбранные идентификаторы в сквозной запрос? Есть ли способ сохранить выбранные идентификаторы в двух или более переменных?

Ответы [ 4 ]

2 голосов
/ 02 апреля 2019

Сохранить значения в нескольких макропеременных, а затем сохранить имена макропеременных в другой макропеременной.

Таким образом, этот код создаст серию макропеременных с именами 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
1 голос
/ 02 апреля 2019

Вы передаете много значений данных, которые появляются в вашем предложении 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;

Если вы не можете получить надлежащие разрешения, вам придется разбить список идентификаторов на куски, а макрос создать серию OR ed 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 ;
);

...    
0 голосов
/ 03 апреля 2019

Другой вариант - записать запрос во временный файл и затем% включить его. Макро-логика не нужна!

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; 
0 голосов
/ 01 апреля 2019

Я предлагаю сначала сохранить все отдельные значения в таблицу, а затем (снова используя proc sql + into) загрузить значения в несколько автономных макропеременных, читая таблицу несколько раз в нескольких наборах. ; на самом деле они должны быть взаимоисключающими, но вместе с тем исчерпывающими.

Есть ли у вас права доступа и СОЗДАТЬ в БД, где находится ваш dbo.REMOTE_TBL? Если это так, вы также можете подумать о копировании WORK.LOCAL_TBL во временную таблицу в БД и сразу запустить внутреннее соединение.

...