макрос стиля функции в SAS с PROC SQL?

string input;
DateTime db;
Console.WriteLine("Enter Date in this Format(YYYY-MM-DD): ");
input = Console.ReadLine();
db = Convert.ToDateTime(input);

//////// this methods convert string value to datetime
///////// in order to print date

Console.WriteLine("{0}-{1}-{2}",db.Year,db.Month,db.Day);
0
задан S420L 13 July 2018 в 20:09
поделиться

4 ответа

Как писал Ричард, макросы в стиле функции испускают код SAS. Общее правило разработки макросов в стиле функции заключается в том, что они содержат только макроязычные выражения. Любой код SAS, который они содержат, будет испускаться. Исторически сложилось так, что это затруднило / раздражало написать макрос функционального стиля, который обрабатывал бы данные, подобные тому, который вы делали бы с помощью шага DATA. К счастью, SAS добавила функцию DOSUBL, которая упрощает запись макросов в стиле функции, которые выполняют код SAS в «боковом сеансе» и испускают результат. См. Статью Рика Лэнгстона .

Вот пример макрофайла в стиле функции, который использовал DOSUBL для подсчета количества записей в таблице и испускает счет. (Это очень неэффективный способ получить показатель записи, просто пример выполнения чего-то в SQL).

%macro SQLcount(table);
  %local rc emit; 

  %let rc=%sysfunc(dosubl(%nrstr(
     proc sql noprint;
      select count(*) into :emit trimmed
      from &table
     quit;
  )));

  &emit 
%mend ;

Его можно использовать как:

proc sql ;
  select name
        ,%SQLcount(sashelp.shoes) as ShoeCount  /*emits 395*/
  from sashelp.class
  ;
quit ;

Когда вышеперечисленный шаг запускается, он вернет 19 строк имен из sashelp.class, а значение ShoeCount будет 395 в каждой строке. Обратите внимание, что макрос SQLcount выполняется только один раз. В то время как этап PROC SQL компилируется / интерпретируется, вызов SQLcount просматривается и выполняется макрос и испускает 395. Шаг становится:

proc sql ;
  select name
        ,395 as ShoeCount  /*emits 395*/
  from sashelp.class
  ;
quit ;

DOSUBL использует «боковой сеанс» для выполнения кода, который позволяет выполнить шаг PROC SQL в боковом сеансе, в то время как основной сеанс интерпретирует шаг PROC SQL.

Я не могу сказать из вашего вопроса, подходит ли этот вариант использования. Возможно, вам нужен макрос функционального стиля, в котором вы могли бы передавать ему значения из таблицы, и макрос выполнялся по каждому значению и возвращал что-то. Предположим, что у вас была таблица, которая была списком имен таблиц, и хотела использовать SQL для подсчета записей в каждой таблице:

data mytables ;
  input table $20. ;
  cards ;
sashelp.shoes
sashelp.class
sashelp.prdsale
;
quit ;

Вы можете сделать это, используя функцию resolve (), чтобы вызывать макросы на основе данных, задерживая выполнение макроса до выполнения оператора SELECT:

proc sql ;
  select table
        ,resolve('%SQLcount('||table||')') as count
  from mytables
  ;
quit ;

При этом SQLcount будет вызываться три раза и будет возвращать количество записей в каждом наборе данных.

table                 count
---------------------------
sashelp.shoes         395
sashelp.class         19
sashelp.prdsale       1440

Вызов макроса не отображается, когда интерпретируется шаг PROC SQL, поскольку он скрыт одиночными кавычками. Затем функция разрешения вызывает макрос, когда выполняется оператор SELECT, передавая значение table в качестве значения параметра, а макрос испускает счетчик записей. Это похоже на подход CALL EXECUTE для использования данных для вызова макросов.

3
ответ дан Quentin 17 August 2018 в 12:10
поделиться
  • 1
    Спасибо, это большая помощь! Тем не менее, у меня все еще есть проблемы с его реализацией. Я хочу передать макрокоманде переменную из таблицы и вернуть ее для каждого наблюдения. Я использовал DOSUBL для формирования моего макроса, как вы описали выше, но когда я использую решение ('% macroName (' || tableVariable || ')') синтаксис, я получаю сообщение об ошибке «конкатенация» требует символьных операндов & quot ;. Это срабатывало при подаче одного значения, не использующего разрешение, и когда я не цитирую tableVariable, он запускается для каждого наблюдения, но ищет «tableVariable» в макросе, а не значение переменной SQL – S420L 17 July 2018 в 15:23
  • 2
    Похоже, что у вас есть числовая переменная или значение, которое вы пытаетесь объединить с || оператора, который не работает. Если это так, вы можете попробовать переключиться на функцию CATS для конкатенации или преобразовать числовую переменную в символ. CATS будет выглядеть так: resolve(cats('%SQLcount(',table,')')) as count – Quentin 17 July 2018 в 16:17
  • 3
    Это сработало отлично, спасибо за помощь! – S420L 17 July 2018 в 16:32

Макрофункции не возвращают значения. Макрофункция может «испускать» исходный код, который

  • является одним или несколькими шагами,
  • , который является фрагментом кода, который будет включен в инструкцию,
  • , который является одним или несколькими инструкциями, которые являются частью шага,
  • и т. д.

Для вашего случая, когда вы хотите «делать» вещи в SQL, вы можете записывать представления SQL, которые затем

  • открываются с помощью %sysfunc(open()) и
  • , обработанных с помощью %sysfunc(set()) и %sysfunc(getvarn()) и %sysfunc(getvarc()).

Не все функциональные возможности SQL могут использоваться этой методикой - select something into :result, это должно быть представление с select something, а макрос будет getvarc читать результат.

Доступ, выполняемый в режиме open / set / get, не приводит к возникновению границы шага, поэтому макропроцессор может продолжить свою логику и в конечном итоге испустить исходный код для потребления уровня фрагмента. (Потребитель - это исполнитель SAS, который обрабатывает макрокод и неявно компилирует и запускает шаги SAS)

0
ответ дан Richard 17 August 2018 в 12:10
поделиться

Похоже, вы можете создать функцию FCMP с помощью proc fcmp. Это в основном способ создания собственных SAS-функций, которые можно использовать в шагах proc sql и data. Например:

/******************************************************************************
** PROGRAM:  COMMON.FCMP_DIV.SAS
**
** DESCRIPTION: PERFORMS A MATHEMATICAL DIVISION BUT WILL RETURN NULL IF THE
**              NUMERATOR OR DENOMINATOR IS MISSING (OR IF THE DIVISOR IS 0).
**
******************************************************************************/

proc fcmp outlib=common.funcs.funcs;

  function div(numerator, denominator);

    if numerator eq . or denominator in (0,.) then do;
      return(.);
    end;
    else do;
      return(numerator / denominator);
    end;

  endsub;
run;

Пример использования (пример - шаг данных, но одинаково хорошо работает в SQL):

data x;
  x1  = div(1,0);
  x2  = div(1,.);
  x3  = div(1,1);

  x4  = div(0,0);
  x5  = div(0,.);
  x6  = div(0,1);

  x7  = div(.,0);
  x8  = div(.,.);
  x9  = div(.,1);

  put _all_;
run;
1
ответ дан Robert Penridge 17 August 2018 в 12:10
поделиться
  • 1
    Я прочитал OP как желающий создать функцию, которая сама выполнит шаг PROC SQL и вернет значение. И эта функция может использоваться в инструкции SQL select или где угодно. Я предполагаю, что с FCMP (и DS2) вы не получаете эту возможность запускать шаг PROC SQL & quot; внутри & quot; еще один шаг SQL, не так ли? – Quentin 17 July 2018 в 16:20
  • 2
    @Quentin Согласен, но я понял, что не знаю больше о том, что они пытаются сделать, возможно, что FCMP может быть полезен в «переопределении» требований. Упоминание об этом может открыть альтернативные пути для ОП. – Robert Penridge 17 July 2018 в 17:56

Вы заявляете, что хотите:

запускать довольно сложную логику при каждом наблюдении набора данных

. Для этого вы должны использовать язык SAS вместо макропроцессора или PROC SQL. Вы можете использовать шаг данных. Или для еще более сложной логики вы должны посмотреть PROC DS2.

1
ответ дан Tom 17 August 2018 в 12:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: