SAS Advanced Certificate

Author

SEOYEON CHOI

Published

August 19, 2025

(합격)!!

Website

시험 기본

시험 구조

  • Performance Based Exam (즉, 문제는 이론 + 코드 기반 실습)

  • 출제 영역

    • SQL (35%)
    • Macro (35%)
    • Advanced Techniques (30%)

1. Accessing Data Using SQL (35%)

  • 기본 SELECT: SELECT, FROM, WHERE, ORDER BY
  • 새 변수 생성: AS, 계산열, CASE WHEN
  • 조인: INNER, LEFT, RIGHT, FULL (COALESCE 함수 포함)
  • Set operators: UNION, OUTER UNION, EXCEPT, INTERSECT
  • 집계: AVG, COUNT, MAX, MIN, SUM, GROUP BY, HAVING
  • 중복 제거: DISTINCT
  • 서브쿼리, 인라인 뷰 활용
  • SAS 확장 기능:
    • Dataset options (KEEP=, DROP=, RENAME=, OBS=)
    • Invocation options (INOBS=, OUTOBS=, NOPRINT, NUMBER)
    • Functions (SCAN, SUBSTR, LENGTH)
    • Dictionary tables (DICTIONARY.COLUMNS 등)
    • CALCULATED 키워드

2. Macro Processing (35%)

  • 매크로 변수
    • 생성: %LET, INTO:, CALL SYMPUTX
    • Scope 제어: %GLOBAL, %LOCAL, SYMPUTX 옵션
    • 이름 구분자(.) 활용
    • 매크로 프로그램
    • 정의/호출: %MACRO … %MEND
  • 파라미터 유무 호출
    • 조건문: %IF-%THEN-%ELSE
    • 반복문: %DO-%END
    • AUTOCALL facility (재사용 가능한 매크로 저장/호출)
  • 매크로 함수
    • 기본: %SCAN, %SUBSTR, %UPCASE
    • Quoting: %STR, %NRSTR
    • Evaluation: %SYSEVALF
    • %SYSFUNC → DATA step 함수 호출
  • 디버깅
    • MLOGIC, MPRINT, SYMBOLGEN
    • %PUT 로그 확인
  • 데이터 기반 매크로
    • && 간접 참조
  • Dictionary table 기반 매크로
    • 반복적 매크로 호출 생성

3. Advanced Techniques (30%)

  • 배열 (Array)
    • 문자형/숫자형 배열
    • DIM 함수
    • Temporary array
    • Dataset 값으로 초기화
  • Hash Object
    • 선언: DECLARE HASH, DECLARE HITER
    • 주요 메서드: DEFINEKEY, DEFINEDATA, DEFINEDONE, FIND, ADD, OUTPUT
    • Iterator: FIRST, NEXT, LAST, PREV
    • 활용: Lookup table, 정렬, 순회
  • Utility Procedures
    • PROC FORMAT – PICTURE 문 (숫자/날짜 포맷 정의, 옵션: round, default, prefix 등)
    • PROC FCMP – 사용자 정의 함수 (단일/다중 인자, 조건문 처리, CMPLIB= 옵션으로 호출)
  • 고급 함수
    • FINDC, FINDW, COUNT, COUNTC, COUNTW
    • LAG
    • Regex 함수 (PRX)
    • 메타문자: () [] {} * + ? . | ^ $
    • 함수: PRXMATCH, PRXPARSE, PRXCHANGE

공식 문제

Question 1

array

  • Open a new programming window to create ACT01.sas in c:.

  • Write a SAS program that will:

    • Create output data set work.ACT01 using sashelp.pricedata as input.
    • Use an array to increase the values of the price1 through price17 variables by 10%.
  • Run your program and troubleshoot as necessary. When you are finished with the project:

    1. Ensure that you have saved your program as ACT01.sas in c:.
    2. From the score.sas program, call the scoreit macro using ACT01 as the parameter: %scoreit(ACT01).
  • What is the value for Response in the SAS log? ___

  • Correct Solution: All price values for all price1-through price17 will be increased by 10%. For example, price2 in observation 5 will now be 126.50. Arrays and do loops would be used in the program.


data work.atc01; 
set sashelp.pricedata; 

    array prices {*} price1-price17; 
    do i = 1 to dim(prices); 
    prices[i] = prices[i] * 1.10; 

    end; 
drop i; 
run;

Question 2

symputx

  • Open a new programming window to create MAC01.sas in c:.

  • Write a DATA step that reads only the first observation of the sashelp.cars data set and stores the value of the Make variable in a macro variable named CarMaker.

  • The macro variable must be defined from within the DATA Step.

  • Run your program and troubleshoot as necessary.

  • When you are finished with the project:

    1. Ensure that you have saved your program as MAC01.sas in c:.
    2. From the score.sas program, call the scoreit macro using MAC01 as the parameter: %scoreit(MAC01).
  • What is the value for Response in the SAS log? __

  • Correct Solution: The CarMaker macro variable will have a value of Acura.

  • The program will include a symputx routine.


data _null_; 
set sashelp.cars; 
    
    if _n_ = 1 then call symputx('Carmaker'.Make); 
    
run;

Question 3

proc sql, mean(), group by

  • Open a new programming window to create SQL01.sas in c:.

  • Write an SQL query that will:

    • Create output data set work.SQL01 using sashelp.cars as input.
    • Compute the average MPG_City for each group of Make.
  • Name the calculated variable AvgCityMPG.

    • The output data should have 2 columns, Make and AvgCityMPG.
  • Run your program and troubleshoot as necessary.

  • When you are finished with the project:

    1. Ensure that you have saved your program as SQL01.sas in c:.
    2. From the score.sas program, call the scoreit macro using SQL01 as the parameter: %scoreit(SQL01).
  • What is the value for Response in the SAS log? __

  • Correct Solution: An SQL query with a group by clause will be written.

  • The AvgCityMPG for MAKE=MINI will be 26.5.


proc sql; 
    create table work.SQL01 as 
    select Make, mean(MPG_City) as AvgCityMPG 
    from sashelp.cars 
    group by Make; 
quit;

Accessing Data Using SQL

기본 SELECT: SELECT, FROM, WHERE, ORDER BY

proc sql;
   select id, 
          name, 
          salary * 12 as Annual_Salary
   from employees
   where department = 'HR' and salary > 40000
   order by Annual_Salary desc;
quit;
  • id, name, Annual_Salary만 출력, employees 테이블에서 HR 부서 & 급여 > 40000인 직원만 뽑음, 연봉 높은 사람 순으로 정렬,

  • FROM employees→ employees 테이블에서 데이터를 불러옴.

  • SELECT id, name salary * 12 as Annual_Salary→ 계산 열(연봉) 생성, AS로 새 이름 부여

  • WHERE department = 'HR' and salary > 40000→ 부서가 HR이고, 월급이 40,000 초과인 직원만 필터링.

  • ORDER BY Annual_Salary desc → 연봉(Annual_Salary) 기준 내림차순 정렬.

새 변수 생성: AS, 계산열, CASE WHEN

  1. AS (Alias, 별칭); 새 변수를 만들거나, 기존 변수에 별칭(alias)을 부여할 때 사용.
  • AS 키워드는 출력 시 컬럼명 변경 + 새 변수 생성 역할.
proc sql;
   select id,
          salary * 12 as Annual_Salary
   from employees;
quit;
  • salary * 12 계산 열을 만들고, 새 변수 이름은 Annual_Salary.
  1. 계산 열 (Computed Column); 기존 컬럼을 이용해 계산된 새로운 컬럼을 만듦.
  • AS와 함께 쓰임.
proc sql;
   select id,
          weight / (height*height) as BMI
   from health;
quit;
  • weight와 height를 활용해서 BMI라는 계산 열 생성.

  • 특징

    • SELECT 문에서만 존재 (원본 데이터셋에는 추가되지 않음).
    • 다른 계산열에 재사용 가능 (단, 순서상 앞에서 정의한 계산 열은 뒤에서 사용 가능).
proc sql;
   select id,
          salary * 12 as Annual_Salary,
          calculated Annual_Salary * 0.1 as Tax
   from employees;
quit;
  • CALCULATED 키워드: 이미 SELECT 안에서 만든 계산 열 재사용.
  1. CASE WHEN; 조건문을 사용하여 새로운 변수를 생성.
  • IF THEN ELSE와 비슷한 역할을 SQL에서 수행.
proc sql;
   select id,
          case 
             when score >= 90 then 'A'
             when score >= 80 then 'B'
             when score >= 70 then 'C'
             else 'F'
          end as Grade
   from exam;
quit;
  • 점수를 등급으로 변환하여 새 변수 Grade 생성.

  • 특징:

    • case로 시작하고 END로 끝남.
    • 반드시 end AS 후 새변수명으로 이름 부여.
    • 문자형/숫자형 혼합 불가 (모두 같은 타입이어야 함).

조인: INNER, LEFT, RIGHT, FULL (COALESCE 함수 포함)

  1. INNER JOIN; 두 테이블에 모두 존재하는 공통 키 값만 결과에 포함.
  • 매칭되지 않는 행은 제외됨.
proc sql;
   select a.id, a.name, b.score
   from tableA as a
        inner join tableB as b
        on a.id = b.id;
quit;
  • tableA와 tableB에 동시에 존재하는 id 값만 결과에 출력됨.
  1. LEFT JOIN; 왼쪽 테이블(A)의 모든 행을 결과에 포함.
  • 오른쪽 테이블(B)에 매칭되는 값이 없으면 NULL로 표시.
proc sql;
   select a.id, a.name, b.score
   from tableA as a
        left join tableB as b
        on a.id = b.id;
quit;
  • tableA의 모든 id가 나오고, 매칭 안 되는 경우 score는 NULL.
    • 문자형은 공백 ’’
    • 숫자형은 결측 .
  1. RIGHT JOIN; 오른쪽 테이블(B)의 모든 행을 결과에 포함.
  • 왼쪽(A)에 매칭되는 값이 없으면 NULL.
proc sql;
   select a.id, a.name, b.score
   from tableA as a
        right join tableB as b
        on a.id = b.id;
quit;
  • tableB의 모든 id가 나오고, 매칭 안 되는 경우 name은 NULL.
    • 문자형은 공백 ’’
    • 숫자형은 결측 .
  1. FULL JOIN; 두 테이블(A, B) 모두의 모든 행을 결과에 포함.
  • 매칭이 없는 경우 NULL 값 채워짐.
proc sql;
   select a.id, a.name, b.score
   from tableA as a
        full join tableB as b
        on a.id = b.id;
quit;
  • tableA와 tableB의 전체 id가 포함되며, 없는 부분은 NULL.
    • 문자형은 공백 ’’
    • 숫자형은 결측 .
  1. COALESCE 함수; NULL 값을 처리하기 위해 사용. 여러 인수 중 처음으로 NULL이 아닌 값을 반환.
  • FULL JOIN 시 특히 자주 사용됨.
proc sql;
   select coalesce(a.id, b.id) as id,
          a.name,
          b.score
   from tableA as a
        full join tableB as b
        on a.id = b.id;
quit;
  • a.id가 없으면 b.id를 대신 사용. 즉, 두 테이블을 FULL JOIN 할 때 키를 깔끔하게 하나로 합칠 수 있음.
    • a.id, b.id 모두 나타내지 않고 id로만

Set operators: UNION, OUTER UNION, EXCEPT, INTERSECT

연산자 중복 처리 컬럼 매칭 특징
UNION 중복 제거 위치 기반 두 SELECT 결과 합침 (DISTINCT 효과, 자동 정렬)
UNION ALL 중복 유지 위치 기반 두 SELECT 결과 합침 (중복 포함, 빠름)
OUTER UNION 중복 유지 위치 기반 컬럼 개수가 달라도 합칠 수 있음, 없는 값은 NULL/결측
OUTER UNION CORR 중복 유지 이름 기반 컬럼 이름 기준으로 매칭, 없는 값은 NULL/결측
EXCEPT 중복 제거 위치 기반 첫 번째 SELECT 결과에서 두 번째 SELECT 결과를 제외
INTERSECT 중복 제거 위치 기반 두 SELECT 결과의 공통 행만 반환
  1. UNION; 두 쿼리 결과를 합침. (중복은 자동 제거)
  • UNION ALL을 쓰면 중복까지 포함.
  • 각 SELECT 절의 컬럼 개수, 타입, 순서가 같아야 함.
proc sql;
   select id, name from tableA
   union
   select id, name from tableB;
quit;
  • tableA와 tableB의 행을 합치되, 중복은 제거.
  1. OUTER UNION; 두 테이블을 세로 + 가로 방향으로 합치는 개념.
  • 공통 변수가 있으면 맞춰서 붙이고, 없는 변수는 NULL로 채움.
  • 즉, 변수 구조가 달라도 가능.
proc sql;
   select id, name from tableA
   outer union corr
   select id, score from tableB;
quit;
  • CORR 옵션: 같은 이름의 컬럼만 정렬해서 붙이고, 없는 값은 NULL.
  1. EXCEPT; 첫 번째 SELECT 결과에서 두 번째 SELECT 결과를 뺀 것.
  • 중복은 제거됨.
proc sql;
   select id from tableA
   except
   select id from tableB;
quit;
  • tableA에는 있지만 tableB에는 없는 id만 출력.
  1. INTERSECT; 두 SELECT 결과에서 공통으로 존재하는 행만 반환.
  • 중복은 제거됨.
proc sql;
   select id from tableA
   intersect
   select id from tableB;
quit;
  • 두 테이블에 모두 존재하는 id만 출력.

집계: AVG, COUNT, MAX, MIN, SUM, GROUP BY, HAVING

  1. 집계 함수 (Aggregate Functions)
함수 설명 예시
AVG( ) 평균 avg(salary)
COUNT( ) 개수 (NULL 제외) count(*) → 전체 행 개수
count(var) → var이 NULL 아닌 행 개수
MAX( ) 최댓값 max(salary)
MIN( ) 최솟값 min(salary)
SUM( ) 합계 sum(salary)
  1. GROUP BY
  • 특정 컬럼을 기준으로 데이터를 그룹화해서 집계 수행.
  • SELECT 절에는 그룹 변수 + 집계 함수만 올 수 있음.
proc sql;
   select department, avg(salary) as Avg_Salary
   from employees
   group by department;
quit;
  • 부서별 평균 급여 계산.
  1. HAVING
  • WHERE와 비슷하지만 집계된 결과에 조건을 걸 때 사용.
  • WHERE은 개별 행(row)에 조건, HAVING은 그룹화된 결과에 조건.
proc sql;
   select department, avg(salary) as Avg_Salary
   from employees
   group by department
   having avg(salary) > 50000;
quit;
  • 평균 급여가 50,000 초과인 부서만 출력.
  1. WHERE vs HAVING 차이
  • WHERE: 집계하기 전에 데이터 필터링
  • HAVING: 집계한 후 그룹 결과 필터링
proc sql;
   /* WHERE 사용 */
   select department, avg(salary) as Avg_Salary
   from employees
   where salary > 30000
   group by department;

   /* HAVING 사용 */
   select department, avg(salary) as Avg_Salary
   from employees
   group by department
   having avg(salary) > 50000;
quit;

group by 사용 하고 안 하고의 차이

  • GROUP BY 없음 → 각 행(row) 그대로 출력
  • GROUP BY 있음 → 지정한 그룹별로 요약(평균, 합계 등 집계 함수 결과)만 출력
  1. GROUP BY 사용하지 않은 경우 (원본 그대로)
Make MPG_City
Toyota 20
Toyota 22
Toyota 25
Ford 15
Ford 18
Honda 30
  1. GROUP BY 사용한 경우 (Make별 평균)
Make AvgCityMPG
Ford 16.5
Honda 30.0
Toyota 22.3

중복 제거: DISTINCT

  1. DISTINCT 기본
  • SELECT 절에서 사용.
  • 결과에서 중복된 행(row)을 제거.
  • 모든 컬럼 조합이 동일하면 한 번만 남음.
proc sql;
   select distinct department
   from employees;
quit;
  • 부서 이름(department)이 중복 없이 한 번씩만 출력.
  1. 여러 컬럼과 함께 사용
  • 여러 변수 조합이 동일하면 중복 제거.
proc sql;
   select distinct department, job_title
   from employees;
quit;
  • (department, job_title) 조합이 같은 경우 한 줄만 출력.
  1. COUNT(DISTINCT …)
  • 특정 변수의 고유값 개수 세기.
  • SAS PROC SQL은 COUNT(DISTINCT col) 지원.
proc sql;
   select count(distinct department) as Dept_N
   from employees;
quit;
  • department의 고유 개수 출력.
  1. DISTINCT와 GROUP BY 차이
  • DISTINCT: 단순히 중복 제거 → 전체 결과를 unique set으로 만듦.
  • GROUP BY: 그룹별 집계 수행 → 집계함수와 함께 사용.
/* DISTINCT */
proc sql;
   select distinct department
   from employees;
quit;

/* GROUP BY */
proc sql;
   select department, avg(salary) as Avg_Sal
   from employees
   group by department;
quit;
  • DISTINCT: 그냥 고유 부서 목록, GROUP BY: 부서별 평균 급여

서브쿼리, 인라인 뷰 활용

  1. 서브쿼리 (Subquery)
  • 다른 SQL문 안에 포함된 하위 SELECT문. 보통 WHERE, HAVING, SELECT 절에서 많이 사용됨.
  1. 단일 값 서브쿼리; 서브쿼리가 하나의 값을 반환.
proc sql;
   select id, name, salary
   from employees
   where salary > (select avg(salary) from employees);
quit;
  • 직원 급여가 전체 평균 급여보다 큰 행만 선택.
  1. 다중 값 서브쿼리 (IN, EXISTS, NOT EXISTS); 서브쿼리가 여러 행을 반환.
  • IN
proc sql;
   select id, name
   from employees
   where department in (select department from departments where region = 'EU');
quit;
  • 유럽(EU) 지역 부서에 속한 직원만 선택.

  • EXIST

proc sql;
   select *
   from tableA as a
   where exists (
      select 1
      from tableB as b
      where a.id = b.id
   );
quit;
  • a.id=b.id 조건을 만족하는 행이 tableB에 존재하면 True
  1. 상관 서브쿼리 (Correlated Subquery); 서브쿼리가 바깥 쿼리의 컬럼과 연결됨.
proc sql;
   select e1.id, e1.salary
   from employees e1
   where salary > (select avg(e2.salary) 
                   from employees e2
                   where e1.department = e2.department);
quit;
  • 각 부서 평균 급여보다 급여가 높은 직원 선택.
  1. 인라인 뷰 (Inline View); FROM 절 안에 들어가는 서브쿼리 (일시적 테이블처럼 사용).
proc sql;
   select department, Avg_Sal
   from (select department, avg(salary) as Avg_Sal
         from employees
         group by department) as dept_avg
   where Avg_Sal > 50000;
quit;
  • 먼저 부서별 평균 급여(dept_avg)를 만든 후, 평균이 50,000 초과인 부서만 선택.

SAS 확장 기능:

Dataset options (KEEP=, DROP=, RENAME=, OBS=)

옵션 역할 예시
KEEP= 지정한 변수만 불러오기/저장 (keep=id name salary)
DROP= 지정한 변수 제외 (drop=phone)
RENAME= 변수명 변경 (rename=(dept=Department))
OBS= 행 개수 제한 (obs=10)
  1. KEEP=; 데이터셋에서 필요한 변수만 선택
  • 입력 시: 읽을 때 해당 변수만 불러옴
  • 출력 시: 결과 데이터셋에 해당 변수만 남김
data emp_keep;
   set employees(keep=id name salary);
run;
  • employees에서 id, name, salary만 가져와서 새 데이터셋 생성
  1. DROP=; 데이터셋에서 제외할 변수 지정
  • 입력 시: 불필요한 변수는 불러오지 않음
  • 출력 시: 결과 데이터셋에서 제외됨
data emp_drop;
   set employees(drop=address phone);
run;
  • employees에서 address, phone 제외하고 나머지 변수 저장
  1. RENAME=; 변수 이름 변경
data emp_rename;
   set employees(rename=(salary=Monthly_Salary dept=Department));
run;
  • salary → Monthly_Salary, dept → Department로 변경
  1. OBS=; 읽어올 관측치 개수 제한
  • 보통 테스트나 샘플링할 때 사용
data emp_obs;
   set employees(obs=10);
run;
  • employees에서 앞의 10개 행만 불러옴
  1. 함께 사용 예시
data emp_final;
   set employees(keep=id name salary dept 
                 drop=phone 
                 rename=(dept=Department) 
                 obs=5);
run;
  • obs=5 → 처음 5개 행만 읽음
  • keep=id name salary dept → 필요한 변수만 선택
  • drop=phone → phone 제외 (이미 keep에 없으므로 영향 없음)
  • rename=dept→Department 적용

Invocation options (INOBS=, OUTOBS=, NOPRINT, NUMBER)

옵션 역할 예시 특징
INOBS= 입력 데이터 행 개수 제한 proc sql inobs=10; 데이터 읽을 때 앞에서 10행만 사용
OUTOBS= 출력 행 개수 제한 proc sql outobs=5; 실행은 전체 대상으로 하지만 결과는 5행만 표시
NOPRINT 결과 출력 생략 proc sql noprint; 로그/결과창에 출력 안 됨, 테이블 생성용에 자주 사용
NUMBER 결과에 행 번호 표시 proc sql number; 결과창에서 행 번호 확인 가능
  1. INOBS=; 입력 행 제한 (읽을 때 앞에서부터 지정한 행 개수만 사용)
proc sql inobs=10;
   select * from employees;
quit;
  • employees에서 앞의 10개 행만 읽어서 SQL 실행.
  1. OUTOBS=; 출력 행 제한 (결과로 보여주는 행 개수 제한)
proc sql outobs=5;
   select * from employees;
quit;
  • employees 전체를 읽지만, 결과는 앞의 5개 행만 출력.
  1. NOPRINT; SQL 실행은 하지만 결과 테이블은 출력하지 않음.
  • 보통 CREATE TABLE이나 INSERT INTO 같은 결과 저장용 쿼리에서 사용.
proc sql noprint;
   create table emp_summary as
   select department, avg(salary) as Avg_Sal
   from employees
   group by department;
quit;
  • 테이블은 생성되지만 로그/출력 창에는 결과 안 뜸.
  1. NUMBER; 결과 출력 시, 각 행 왼쪽에 행 번호를 붙여줌.
proc sql number;
   select id, name, salary
   from employees;
quit;
  • 결과창에 1, 2, 3… 번호가 붙어서 출력됨.

Functions (SCAN, SUBSTR, LENGTH)

함수 역할 예시 결과
SCAN 구분자로 나눈 n번째 단어 추출 scan("a,b,c",2,",") "b"
SUBSTR 문자열에서 특정 위치 부분 추출/수정 substr("ABCDEF",2,3) "BCD"
LENGTH 문자열 길이 반환 length("Hello") 5
  1. SCAN; 문자열에서 단어 단위로 추출

scan(string, n <, delimiters>)

  • string: 문자 변수
  • n: 몇 번째 단어를 추출할지
  • delimiters: 구분자 (기본은 공백, , 등 지정 가능)
data _null_;
   x = "SAS Advanced Certification";
   word1 = scan(x,1);   /* SAS */
   word2 = scan(x,2);   /* Advanced */
   word3 = scan(x,3);   /* Certification */
   put word1= word2= word3=;
run;
  • 시험에서는 이메일 주소에서 @ 앞부분 추출 같은 문제가 자주 나옴.

  • scan("user@test.com", 1, "@") → “user”

  • scan("user@test.com", 2, "@") → “test.com”

  1. SUBSTR; 문자열에서 일정 위치부터 지정 길이만큼 잘라내기

substr(string, start <, length>)

  • start: 시작 위치 (1부터 시작)
  • length: 잘라낼 문자 수 (생략하면 끝까지)
data _null_;
   x = "ABCDEFG";
   y1 = substr(x,2,3);  /* BCD */
   y2 = substr(x,5);    /* EFG */
   put y1= y2=;
run;
  • 주의: SUBSTR 함수는 할당문 좌변에 올 수 있음 → 문자열 일부 수정 가능!
data _null_;
   x = "12345";
   substr(x,2,2) = "AB";   /* x = 1AB45 */
   put x=;
run;
  1. LENGTH; 문자열의 길이(문자 개수) 반환

length(string)

data _null_;
   x = "Hello";
   len = length(x);   /* 5 */
   put len=;
run;
  • LENGTH는 공백도 포함
  • LENGTHN = 문자열 길이, NULL → 0
  • LENGTHC = 문자 변수의 메모리 길이 반환 (실제 저장 공간)

Dictionary tables (DICTIONARY.COLUMNS 등)

Dictionary Table SASHELP 뷰 주요 정보
COLUMNS VCOLUMN 변수명, 타입, 길이 등
TABLES VTABLE 데이터셋 행 수, 생성·수정일
LIBNAMES VLIBNAM 라이브러리 경로, 엔진
MEMBERS VMEMBER 라이브러리 내 멤버
OPTIONS VOPTION 시스템 옵션
MACROS VMACRO 매크로 변수
INDEXES VINDEX 인덱스 정보
  1. Dictionary Tables
  • SAS 세션에서 사용할 수 있는 메타데이터 테이블

    • 데이터 구조를 미리 확인하기 좋음
  • 라이브러리, 데이터셋, 변수, 인덱스, 포맷, 옵션 등에 대한 정보를 담고 있음

  • 조회할 때는 PROC SQL 사용

  • 특징

    • DICTIONARY. 라이브러리로 직접 접근 가능
    • 같은 내용을 SASHELP. 뷰로도 조회 가능 (SASHELP.VCOLUMN 등)
  1. 주요 Dictionary Tables
테이블 이름 내용
DICTIONARY.COLUMNS 데이터셋의 변수 정보 (이름, 타입, 길이 등)
DICTIONARY.TABLES 라이브러리 내 테이블 정보 (행 수, 수정일 등)
DICTIONARY.LIBNAMES 현재 할당된 라이브러리 정보
DICTIONARY.MEMBERS 모든 SAS 라이브러리의 멤버(데이터셋, 뷰 등)
DICTIONARY.INDEXES 인덱스 정보
DICTIONARY.OPTIONS 현재 세션의 시스템 옵션 값
DICTIONARY.MACROS 매크로 변수 정보
DICTIONARY.ENGINES 라이브러리 엔진 정보
  1. 활용 예시
  1. 컬럼(변수) 정보 조회
proc sql;
   select libname, memname, name, type, length
   from dictionary.columns
   where libname='WORK' and memname='EMPLOYEES';
quit;
  • WORK 라이브러리의 EMPLOYEES 데이터셋 변수 정보 조회
변수명 의미
libname 라이브러리 이름 (예: WORK, SASHELP)
memname 데이터셋 이름
name 변수 이름
type 변수 유형 (char 또는 num)
length 변수 길이 (문자는 바이트 단위, 숫자는 8이 기본)
label 변수 라벨 (있을 경우)
format 변수 서식
informat 입력 서식
varnum 변수의 순서 (데이터셋 내 위치)
  1. 데이터셋(테이블) 정보 조회
proc sql;
   select libname, memname, nobs, crdate, modate
   from dictionary.tables
   where libname='WORK';
quit;
  • WORK 라이브러리에 있는 모든 데이터셋의 행 수, 생성일(create date), 수정일(moddify date) 확인
  1. 라이브러리 정보 확인
proc sql;
   select libname, path, engine
   from dictionary.libnames;
quit;
  • 라이브러리 위치 및 엔진 확인

Macro Processing

생성: %LET, INTO:, CALL SYMPUTX

방법 사용 위치 생성 범위 용도
%LET 매크로 언어 직접 전역 고정 상수/문자 할당
INTO: PROC SQL 전역 SQL 결과 → 매크로 변수
CALL SYMPUTX DATA step 기본 전역 (지역도 가능) 데이터 값 → 매크로 변수
  1. %LET; 전역 매크로 변수를 생성
  • 간단히 상수 값이나 문자열을 할당할 때 사용
%let today = 2025-09-06;
%put &=today;
  • 항상 전역 변수(global)
  • 문자/숫자 상관없이 문자열처럼 저장
  • 공백 포함 시 따옴표 사용 필요 (%let name=“My Name”;)
  1. INTO:; PROC SQL 안에서 결과를 매크로 변수로 저장
  1. 단일 값 저장
proc sql noprint;
   select avg(salary)
   into :avg_sal
   from employees;
quit;

%put &=avg_sal;
  • 평균 급여를 &avg_sal 매크로 변수에 저장
    • 만약 AVG_SAL이 5라면,
    • AVG_SAL=5
    • 이렇게 결과가 나옴.
  1. 여러 값 저장

공백 구분:

proc sql noprint;
   select name
   into :namelist separated by ' '
   from employees;
quit;

%put &=namelist;
  • 만약 NAMELIST=Alice Bob Carol라면, Alice Bob Carol 이런 식으로 저장

  • 그래서 결과는 NAMELIST=Alice Bob Carol 이렇게 나옴

  • 여러 개 매크로 변수에 순서대로 할당:

proc sql noprint;
   select name
   into :name1-:name3
   from employees;
quit;

%put &name1 &name2 &name3;
  1. CALL SYMPUTX; DATA step에서 매크로 변수 생성
data _null_;
   set employees;
   call symputx('empname', name);
run;

%put &=empname;
  • 마지막 관측치의 name 값이 &empname에 저장

  • 행 단위 반복 가능 → 관측치별 매크로 변수 만들 수 있음

  • SYMPUTX는 공백 제거하고, 자동으로 문자/숫자 처리

  • SYMPUT보다 SYMPUTX를 쓰는 게 권장됨

다음 코드에서 maxage라는 매크로 변수를 생성하시오.

data _null_;
   set sashelp.class end=last;
   retain maxage 0;
   if Age > maxage then maxage=Age;
   if last then call symputx('maxage', maxage);
run;

%put &=maxage;

Scope 제어: %GLOBAL, %LOCAL, SYMPUTX 옵션

방법 생성 범위 특징
%GLOBAL 전역 항상 전역 매크로 변수 생성 (세션 전체에서 유효)
%LOCAL 지역 매크로 안에서만 사용, 매크로 종료 시 삭제
CALL SYMPUTX 기본 전역, 옵션으로 제어 'G' 전역, 'L' 지역 / 기본은 실행 환경 따라 자동 결정
  1. 매크로 변수 스코프 개념
  • 전역(Global): SAS 세션 내내 유지, 모든 매크로에서 접근 가능
  • 지역(Local): 특정 매크로 실행 중에만 존재, 매크로 종료 시 자동 삭제
  1. %GLOBAL
  • 전역 매크로 변수를 명시적으로 선언
  • 매크로 외부·내부 어디서든 사용 가능
%global gvar;
%let gvar = 100;
%put &=gvar;   /* 100 */
  • 이미 존재하는 전역 변수면 값은 유지, 없으면 새로 생성
  1. %LOCAL
  • 지역 매크로 변수 선언 (해당 매크로 안에서만 유효)
%macro test;
   %local lvar;
   %let lvar = 200;
   %put &=lvar;
%mend;
%test;
%put &=lvar; 
  • 매크로 종료 시 자동 소멸
  1. CALL SYMPUTX 옵션
  • DATA step에서 매크로 변수 생성 시 스코프 제어 가능
%macro demo;
   data _null_;
      x = 300;
      call symputx('mvar', x, 'L'); /* Local */
   run;
   %put Local var = &mvar;
%mend;
%demo;
  • 세 번째 인수(스코프 지정):
    • ‘G’ → 전역(Global) 매크로 변수 생성
    • ‘L’ → 지역(Local) 매크로 변수 생성
  • 생략 시:
    • 매크로 실행 중이면 지역(local)
    • 아니면 전역(global)

이름 구분자(.) 활용

  1. 기본 예시
%let var = name;

%put &var1;    /* 매크로 변수 var1 찾음 → 정의 안 되어 있으면 경고 */
%put &var.1;   /* 매크로 변수 var 찾고, 뒤에 "1" 붙임 → name1 */
  • &var1 은 var1이라는 매크로 변수를 찾음.
  • &var.1 은 var 매크로 변수의 값(name) + 1 = name1
  1. 문자열 연결할 때
%let month = Jan;

%put &month.Sales;   /* 매크로 변수 monthSalses 찾으려 함 → 에러 */
%put &month..Sales;   /* month → Jan + Sales → JanSales */
  • &month.Sales 처럼 구분자 붙여야 원하는 결과 나옴.
  1. 루프에서 자주 쓰임
%macro loop;
   %do i=1 %to 3;
      %let var&i = value&i;
      %put &&var&i;    /* && → &var1, &var2 ... */
   %end;
%mend;
%loop;
  • 이때도 &var.&i 혹은 &var&i 로 처리해야 함.
  • .을 붙이면 매크로 변수명과 루프 인덱스 구분이 확실해짐.

매크로 프로그램

정의/호출: %MACRO … %MEND

%macro 매크로이름(매개변수);
   /* 매크로 코드 */
%mend 매크로이름;
  • %macro ~ %mend 사이에 매크로 프로그램 코드 작성
  • 호출할 때는 %매크로이름(인수) 형태로 실행
  1. 매크로 프로그램 생성과 호출
  1. 매개변수 없는 매크로
%macro hello;
   %put Hello, SAS Macro!;
%mend hello;

%hello;
  • %hello; 실행 시 로그에 Hello, SAS Macro! 출력
  1. 매개변수 있는 매크로
%macro greet(name);
   %put Hello, &name!;
%mend greet;

%greet(Name);
  • %greet(Name); 실행 시 Hello, Name! 출력

파라미터 유무 호출

  1. 매개변수의 종류
  • 위치 매개변수(Positional parameter)
%macro report(dept, year);
   %put Department=&dept, Year=&year;
%mend;
%report(HR, 2025);
  • 키워드 매개변수(Keyword parameter); 기본값 지정 가능
%macro report(dept=Sales, year=2025);
   %put Department=&dept, Year=&year;
%mend;
%report();                /* Sales, 2025 */
%report(dept=HR, year=2024); /* HR, 2024 */
  1. 조건문과 반복문

조건문: %IF-%THEN-%ELSE

  1. 조건문 %IF … %THEN … %ELSE
%macro check(num);
   %if &num > 0 %then %put Positive;
   %else %put Non-positive;
%mend;
%check(10);

minoperator

  • minoperator는 in을 쓸 수 있게 해줌
%macro avgfuel(loc) / minoperator;
%else %if &loc in ASIA EUROPE %then %do;

반복문: %DO-%END

  1. 반복문 %DO ~ %END
%macro loop(n);
   %do i=1 %to &n;
      %put Iteration &i;
   %end;
%mend;
%loop(3);
  1. 매크로 프로그램 활용 예시
%macro summary(dataset, var);
   proc sql;
      select mean(&var), max(&var), min(&var)
      from &dataset;
   quit;
%mend;

%summary(sashelp.class, height);
  • sashelp.class 데이터에서 height 변수의 평균, 최댓값, 최솟값 출력

AUTOCALL facility (재사용 가능한 매크로 저장/호출)

/* AUTOCALL 세팅 */
options mautosource
        sasautos = ("c:\cert\programs" "!sasroot\core\sasmacro");

/* c:\cert\programs\greet.sas 파일 내용:
   %macro greet(name);
      %put Hello, &name!;
   %mend;
*/

/* 호출 */
%greet(Max);
  • Hello, Max!

매크로 함수

기본: %SCAN, %SUBSTR, %UPCASE

함수 역할 예시 결과
%SCAN 구분자로 n번째 단어 추출 %scan(red,blue,green,2,%str(,)) blue
%SUBSTR 문자열 부분 추출 %substr(Advanced,1,3) Adv
%UPCASE 대문자 변환 %upcase(London) LONDON
  1. %SCAN; 문자열에서 단어 단위 추출 (매크로 함수 버전)
%let str = red,blue,green;

%put %scan(&str,1,%str(,));   /* red   */
%put %scan(&str,2,%str(,));   /* blue  */
%put %scan(&str,3,%str(,));   /* green */
  • 데이터 함수 SCAN과 유사하지만 매크로 처리 단계에서 실행
  • 구분자(delimiters) 지정 가능 (%str(,) 처럼 특수문자 처리 필요)
  1. %SUBSTR; 문자열의 특정 위치 부분 추출
%let word = Advanced;

%put %substr(&word,1,3);   /* Adv */
%put %substr(&word,5);     /* nced (길이 생략 시 끝까지) */
  • 데이터 함수 SUBSTR와 달리 매크로 함수는 수정 불가, 추출 전용

  • 시작 위치는 1부터 시작

  • 매크로 변수 name=Alexander에서 앞 4글자만 추출하여 매크로 변수 shortname을 만드시오.

    • %SUBSTR(string, position, length)
    • 결과: Alex
%let name=Alexander;
%let shortname=%substr(&name,1,4);
%put &=shortname;
  1. %UPCASE; 문자열을 모두 대문자로 변환
%let city = London;

%put %upcase(&city);   /* LONDON */
  • 데이터 함수 UPCASE와 동일한 기능을 매크로 환경에서 수행
  • 시험에서는 주로 비교 시 대소문자 무시 처리 용도로 등장
  1. %LOWCASE; 문자열을 모두 소문자로 변환
%let city = LONDON;

%put %lowcase(&city);   /* london */
  1. %CMPCASE; 문자열의 앞글자만 대문자로 변환
%let city = LONDON;

%put %cmpcase(&city);   /* London */

Quoting: %STR, %NRSTR

  • 매크로 언어에서 특수문자, 공백, 예약어를 “그대로 문자로 취급”하게 해줌.
  • SAS는 기본적으로 , ; ’ ” ( ) 같은 문자를 코드 구분자로 해석하려고 하는데, quoting을 쓰면 문자 그대로 저장 가능.
함수 역할 처리 가능한 문자 예시 결과
%STR 특수문자/공백을 문자열로 저장 공백, , ; ( ) %str(New York) New York
%NRSTR %, & 같은 매크로 trigger까지 문자로 저장 위 + %, & %nrstr(%put Hello;) %put Hello; (실행 안 됨)
  1. %STR; 특수문자나 공백을 매크로 상수로 저장할 때 사용.
%let city = %str(New York);
%put &=city;   /* New York */
  • 공백, 세미콜론(;), 괄호, 쉼표(,) 같은 구분 문자를 문자 그대로 저장
  • ) & % 같은 매크로 trigger 문자는 처리 못함
  1. %NRSTR; %STR과 동일하지만, 매크로 trigger 문자(&, %)도 문자 그대로 저장
%let code = %nrstr(%put Hello;);
%put &=code;   /* %put Hello; */
  • % %put, &macrovar 같은 것까지 문자 그대로 저장 → 실행 안 됨
  • 보통 매크로 디버깅, 코드 조각 저장 등에 쓰임
/* %STR */
%let msg1 = %str(Hello; World);
%put &=msg1;     /* Hello; World */

/* %NRSTR */
%let msg2 = %nrstr(%put Hello;);
%put &=msg2;     /* %put Hello;  (실행 안 됨, 문자열로 저장됨) */

Evaluation: %SYSEVALF

  • 매크로 환경에서 산술 계산을 할 때 사용
  • 특히 소수점(부동소수점) 계산이 필요할 때 반드시 필요
함수 특징 예시 결과
%EVAL 정수 계산만 가능 %eval(5/2) 2
%SYSEVALF 실수 계산 가능 %sysevalf(5/2) 2.5
%SYSEVALF(...,BOOLEAN) 조건식 판정 %sysevalf(5>3,boolean) 1
%put %sysevalf(3.2 + 4.8);   /* 8.0 */
%put %sysevalf(10/3);        /* 3.33333 */
  • 매크로 변수 계산도 가능
%let a = 5;
%let b = 2;

%let result = %sysevalf(&a / &b);
%put &=result;   /* 2.5 */
  • 옵션

%SYSEVALF(expression <,conversion-type>) - conversion-type: 계산 결과를 어떻게 반환할지 결정

옵션 설명 예시
BOOLEAN 0/1 반환 (조건식 판정) %sysevalf(5>3,boolean) → 1
CEIL 올림 %sysevalf(3.2,ceil) → 4
FLOOR 내림 %sysevalf(3.8,floor) → 3
INTEGER 정수 변환 (소수 버림) %sysevalf(3.8,integer) → 3
  • %EVAL → 정수 연산만 가능 (소수점 불가)
  • %SYSEVALF → 부동소수점 연산 가능
%put %eval(5/2);        /* 2 */
%put %sysevalf(5/2);    /* 2.5 */

관련 문제

  • 모두 매크로 코드로 x초기값1.25로 지정하고 do until이나 do while로 2보다 작은 값에 댜해서만 x log기록 후 0.25씩 늘려가는 매크로
    • %sysevalf 입력!

%macro loopx;

    %global x;
    %let x = 1.25;
    
   %do %while(&x < 2);
       &x = %sysevalf(&x + 0.25);
   %end;
%mend loopx;

%loopx

구문 조건 검사 시점 최소 반복 반복 조건 주요 용도
DO WHILE(condition); 루프 시작 전 0회 조건이 참이면 계속 사전 조건 확인
DO UNTIL(condition); 루프 끝난 후 1회 조건이 참이 되면 종료 최소 1회 실행 보장

%SYSFUNC → DATA step 함수 호출

  • 매크로 환경에서 DATA step 함수를 호출할 수 있게 해줌
  • 즉, 매크로 코드에서도 숫자, 문자 처리 함수를 직접 활용 가능
구분 예시 결과
숫자 함수 %sysfunc(mean(5,10,15)) 10
날짜 함수 %sysfunc(today(),date9.) 06SEP2025
문자 함수 %sysfunc(upcase(text)) TEXT
중첩 사용 %sysfunc(upcase(%sysfunc(reverse(abc)))) CBA

%sysfunc(function-name <(arguments)> <,format>)

  • function-name : DATA step 함수 이름
  • arguments : 함수 인자
  • format : 결과를 출력할 때 적용할 SAS 포맷(선택 사항)
  1. 숫자 함수 호출
%put %sysfunc(mean(5,10,15));     /* 10 */
%put %sysfunc(round(3.14159,0.01)); /* 3.14 */
  1. 날짜 함수 호출
%put %sysfunc(today(),date9.);   /* 06SEP2025 */
%put %sysfunc(time(),time8.);    /* 14:35:12 같은 형식 */
  • 포맷을 뒤에 붙이면 사람이 읽기 쉽게 출력 가능
  1. 문자 함수 호출
%let text = python;
%put %sysfunc(upcase(&text));   /* PYTHON */
%put %sysfunc(reverse(&text));  /* nohtpyp */
  • 고급 사용 – 매크로 변수에 저장
%let curdate = %sysfunc(today(),yymmddn8.);
%put &=curdate;   /* 20250906 */
  • 오늘 날짜를 YYYYMMDD 형태로 매크로 변수에 저장

macro 변수 저장

  • sashelp.class에서 나이가 16인 학생들의 이름을 매크로 변수 student_list에 공백으로 구분해 저장하시오.
proc sql noprint;
   select Name into :student_list separated by ' '
   from sashelp.class
   where Age=16;
quit;

%put &=student_list;

매크로 지우는 코드

  • %symdel
%symdel &vars;

디버깅

MLOGIC, MPRINT, SYMBOLGEN

옵션 역할 로그에 출력되는 내용 예시
MLOGIC 매크로 실행 흐름 표시 매크로 시작/끝, %IF 조건 평가 %if가 TRUE/FALSE로 나오는지 확인
MPRINT 매크로가 생성한 SAS 코드 표시 실제 실행된 DATA/PROC 구문 확장된 코드 확인
SYMBOLGEN 매크로 변수 치환 과정 표시 &var → 값 매크로 변수 값 확인
  1. MLOGIC
  • 매크로 실행 시, 매크로의 논리 흐름(logic)을 로그에 출력
  • 어떤 매크로가 호출됐는지, %IF 조건이 어떻게 평가됐는지 보여줌
options mlogic;

%macro test(x);
   %if &x > 10 %then %put Greater than 10;
   %else %put 10 or less;
%mend;

%test(5);
  • log
MLOGIC(TEST):  Beginning execution.
MLOGIC(TEST):  %IF condition &x > 10 is FALSE
MLOGIC(TEST):  Ending execution.
  1. MPRINT
  • 매크로 실행 시, 실제로 실행된 SAS 코드를 로그에 출력
  • 매크로가 어떤 DATA/PROC 구문으로 확장(expand)되는지 확인 가능
options mprint;

%macro step;
   proc print data=sashelp.class(obs=5);
   run;
%mend;

%step;
  • log
MPRINT(STEP):   proc print data=sashelp.class(obs=5);
MPRINT(STEP):   run;
  1. SYMBOLGEN
  • 매크로 변수 치환 과정을 로그에 출력
  • 어떤 값으로 변환되는지 확인 가능
options symbolgen;

%let name = Name;
%put Hello, &name;
  • log
SYMBOLGEN:  Macro variable NAME resolves to Name

%PUT 로그 확인

  • 매크로 코드에서 메시지를 SAS 로그에 출력
  • 주로 디버깅, 매크로 변수 값 확인에 사용
사용법 설명 예시 결과
%put &var; 매크로 변수 값 출력 %put &city; London
%put &=var; 변수명=값 출력 %put &=city; CITY=London
%put NOTE: 로그 메시지 레벨 지정 %put NOTE: Hello; NOTE: Hello
%put _all_; 모든 매크로 변수 표시 %put _all_; 전역/자동 변수 목록
%let name = Name;
%put Hello, &name;
  • log
Hello, Name
  1. 매크로 변수 값 확인
%let city = London;
%put &=city;
  • log
CITY=London
  • &=변수명 형태로 쓰면 변수명=값 형태로 깔끔하게 출력됨
  1. 사용자 정의 메시지
%put NOTE: Macro started!;
%put WARNING: Check input dataset;
%put ERROR: Something went wrong;
  • log
NOTE: Macro started!
WARNING: Check input dataset
ERROR: Something went wrong
  • 실제 SAS 로그의 NOTE / WARNING / ERROR 스타일로 표시됨
  1. 시스템 매크로 변수 출력
%put _automatic_;   /* 자동 생성된 매크로 변수 */
%put _user_;        /* 사용자가 만든 매크로 변수 */
%put _all_;         /* 모든 매크로 변수 */
GLOBAL CITY London
AUTOMATIC SYSDATE9 06SEP2025

%put

  • % put error:쓰고 log에 print 안 되게 하는 법
%let a=0;

%macro test;
   %if &a %then %put ERROR: Something wrong.;
%mend;
%test;
  • a가 0이면 에러 log안 나오게

  • USER 키워드는 사용자가 만든 모든 매크로 변수 + global + local를 로그에 보여줌

%let global1=AAA;

%macro demo;
   %let local1=BBB;
   %global global2;
   %let global2=CCC;

   %put ---- Inside macro ----;
   %put _USER_;
%mend;

%demo

%put ---- After macro ----;
%put _USER_;
  • log 결과
GLOBAL GLOBAL1 AAA
GLOBAL GLOBAL2 CCC
LOCAL  LOCAL1 BBB

---- After macro ----
GLOBAL GLOBAL1 AAA
GLOBAL GLOBAL2 CCC
  • %put _ALL_; → 자동변수 + 사용자 변수 + 시스템 변수 전부 다
  • %put _AUTOMATIC_; → 자동으로 제공되는 시스템 매크로 변수만
  • %put _GLOBAL_; → 글로벌 변수만
  • %put _LOCAL_; → 매크로 실행 중일 때만 로컬 변수만
proc sql noprint;
    select name
    into :namelist separated by ','
    into :n1 - :n5
    into :avg_h, :avg_w
    from sashelp.class
    where sex='F';
quit;
  1. 디버깅 활용
  • 보통 MLOGIC, MPRINT, SYMBOLGEN 옵션과 같이 %PUT을 넣어 매크로 변수 값이 제대로 넘어오는지 확인
%macro demo(x);
   %put NOTE: Parameter X = &x;
%mend;

%demo(100);
  • log
NOTE: Parameter X = 100

put vs putlog

  • put
    • %LET → 매크로 변수 name 생성 (Name)
    • %PUT → 매크로 실행 시점에 로그에 문자열 출력
    • 결과 (로그):
%let name=Mike;
%put Hello, &name.;

결과: Hello, Mike

  • put log
    • DATA step 실행 중, putlog 문이 SAS 로그에 메시지 기록
    • “현재 값은” → 문자열 그대로 출력
    • x= y= → 변수 이름과 값 함께 출력
    • 결과 (로그):
data _null_;
   x = 10;
   y = 20;
   putlog "현재 값은 " x= y=;
run;

결과: 현재 값은 x=10 y=20

데이터 기반 매크로

간접참조&&

  • 매크로 변수 이름을 다른 매크로 변수 값으로부터 동적으로 만들어서 참조하는 것.

  • && 조합은 치환 과정에서 단계적으로 풀리면서 최종 매크로 변수로 변환됨.

  • 아래와 같은 매크로 변수들이 있을 때, var1=Height, var2=Weight, var3=Age. 루프를 이용해 차례대로 변수명을 출력하시오.

    • &&var&i → &var1 → Height, 두 번 풀려서 최종적으로 변수명이 나옴
%let var1=Height;
%let var2=Weight;
%let var3=Age;

%macro show;
   %do i=1 %to 3;
      %put &&var&i;
   %end;
%mend;

%show

Dictionary table 기반 매크로

반복적 매크로 호출 생성

  1. 간단한 반복문으로 매크로 호출
%macro call_loop;
   %do i=1 %to 3;
      %put This is loop number &i;
   %end;
%mend;

%call_loop;
  • log
This is loop number 1
This is loop number 2
This is loop number 3
  1. 매크로 변수 리스트 기반 반복 호출
%let city1=Seoul;
%let city2=London;
%let city3=Paris;

%macro city_loop;
   %do i=1 %to 3;
      %put City: &&city&i;
   %end;
%mend;

%city_loop;
City: Seoul
City: London
City: Paris
  • 여기서 간접참조(&&) 활용됨 → &&city&i → &city1, &city2, &city3
  1. SQL INTO: 이용해서 매크로 호출 생성
  • 데이터셋 값으로 반복 호출할 수도 있음.
proc sql noprint;
   select name
   into :namelist separated by ' '
   from sashelp.class(obs=3);
quit;

%macro run_names;
   %let n=%sysfunc(countw(&namelist));
   %do i=1 %to &n;
      %let one=%scan(&namelist,&i);
      %put Student: &one;
   %end;
%mend;

%run_names;
Student: Alfred
Student: Alice
Student: Barbara
  1. 매크로 호출 자체를 반복적으로 생성
  • 예를 들어 proc print를 여러 데이터셋에 대해 자동 실행:
%macro print_multi(dsnlist);
   %let n=%sysfunc(countw(&dsnlist));
   %do i=1 %to &n;
      %let one=%scan(&dsnlist,&i);
      proc print data=&one(obs=3);
      run;
   %end;
%mend;

%print_multi(sashelp.class sashelp.cars sashelp.iris);
  • 3개 데이터셋 각각에 대해 proc print 실행

오늘 날짜 표현 방법

  • 방법 1: %SYSFUNC + date()
    • date() 함수 → 현재 날짜 (SAS 일련번호, 1960/01/01 기준)
    • date9. 포맷 → 26AUG2025 형태
%let today=%sysfunc(date(), date9.);
%put &=today;
  • 방법 2: %SYSFUNC + today()
    • today() 함수도 현재 날짜 반환 (date()와 동일)
    • 다른 포맷 사용 가능 (yymmdd10. → 2025-08-26)
%let today=%sysfunc(today(), yymmdd10.);
%put &=today;
  • 방법 3: datetime()
    • 현재 날짜+시간 출력 (26AUG2025:15:30:45)
%let now=%sysfunc(datetime(), datetime20.);
%put &=now;
  • 방법 4: %sysfunc(today(), worddate.)
    • 결과: 26 August 2025 (가독성 좋은 문자열)
%let today=%sysfunc(today(), worddate.);
%put &=today;
  • 방법 5: %sysfunc(today(), weekdate.)
    • 결과: Tuesday, August 26, 2025
%let today=%sysfunc(today(), weekdate.);
%put &=today;
  • 방법 6: 시스템 매크로 변수 활용 (&sysdate9, &sysdatetime)
    • SAS 자동 제공 시스템 매크로 변수 사용
    • 형식은 고정되어 있음
%put &=sysdate9;      /* SYSDATE9=16OCT2025 */
%put &=sysdate;       /* SYSDATE=26AUG25   */
%put &=sysdatetime;   /* SYSDATETIME=26AUG25:15:30:45 */
구문 의미 결과 예시
%put &var; 변수 값만 출력 London
%put &=var; 변수 이름과 값을 함께 출력 VAR=London

Advanced Techniques

배열 (Array)

항목 설명 예시
문자형/숫자형 배열 $ 기호 여부로 문자/숫자형 구분 array names[3] $ name1-name3;
DIM 함수 배열 길이 반환 dim(scores)
Temporary array 변수와 연결 안 됨, 메모리에만 존재 array temp[3] _temporary_;
Dataset 값 초기화 데이터셋 값이나 직접 값으로 초기화 array nums[5] (10 20 30 40 50);

문자형/숫자형 배열

숫자형 배열 (기본)

data example;
   array scores[3] score1-score3;
   input score1-score3;
   datalines;
10 20 30
;
run;
  • score1, score2, score3가 배열 scores[1], scores[2], scores[3]로 참조 가능

문자형 배열

  • array 선언 시 $ 기호로 문자형 선언
data example;
   array names[3] $ name1-name3;
   input name1 $ name2 $ name3 $;
   datalines;
Kim Lee Park
;
run;
  • names[1] = Kim, names[2] = Lee, names[3] = Park

DIM 함수

배열의 크기(요소 개수) 반환

data example;
   array nums[5] (1 2 3 4 5);
   do i=1 to dim(nums);
      put nums[i]=;
   end;
run;
  • DIM(arrayname) → 배열 길이
  • LBOUND / HBOUND도 있음 (배열 시작/끝 인덱스)

Temporary array

데이터셋 변수와 연결되지 않고, 프로그램 실행 중에만 존재하는 배열 (메모리에서만 사용)

data temp_array;
   array temp[3] _temporary_;
   do i=1 to 3;
      temp[i] = i*10;
      put temp[i]=;
   end;
run;
  • 실제 데이터셋에는 저장되지 않음
  • 반복 계산용, lookup table 용도로 활용

Dataset 값으로 초기화

배열을 데이터셋 값으로 채움 (array + set)

data init_array;
   set scores_dataset;  /* dataset의 값 읽어오기 */
   array scores[3] score1-score3;
   do i=1 to dim(scores);
      put scores[i]=;
   end;
run;

또는 DATA step 안에서 직접 초기화

data init_array;
array nums[5] (10 20 30 40 50);
   do i=1 to dim(nums);
      put nums[i]=;
   end;
run;

시험에서

  • 이렇게 하면 7,8,9 이렇게 3개 가져옴
array Farenht [7:9] Temp7-Temp9;
array Celsius [7:9] TempC7-TempC9;
  • 또는 이렇게 해서 한 번에 4개 나열
array Status[4] $ 5 StatusQ1-StatusQ4;
  • 2행 3열로 나타남
array Avg [2013:2014,3] (40.9, 40.7, 38.6, 42.5, 42.6, 45.4): 
  • N = 1일 때만 작동 → 즉, 한 번의 데이터 스텝 실행 중에 array_data의 두 줄 모두 처리함.

  • set array_data는 두 번 호출되며 각 줄을 Row=1, Row=2로 저장

  • 배열의 메모리 배치 순서: TwoD[1,1] → TwoD[1,2] → TwoD[1,3] → TwoD[2,1] → TwoD[2,2] → TwoD[2,3]

  • Row=1 → OneD = Jack, Mary, Sally

    • TwoD[1,1] = Jack
    • TwoD[1,2] = Mary
    • TwoD[1,3] = Sally
  • Row=2 → OneD = Christy, John, Marty

    • TwoD[2,1] = Christy
    • TwoD[2,2] = John
    • TwoD[2,3] = Marty
data new_data(drop=Row Column);
  array TwoD[2,3] $;           /* 2행 3열 문자형 배열 선언 */
  retain TwoD1-TwoD6;          /* 배열 값을 유지 */
  
  if _N_ = 1 then do Row = 1 to 2;   /* 최초 실행 시 2개의 행 처리 */
    set array_data;                 /* 한 줄씩 읽음 */
    
    array OneD[3] Name1-Name3;      /* Name1~Name3을 1차원 배열로 묶음 */
    
    do Column = 1 to 3;
      TwoD[Row, Column] = OneD[Column];   /* 값을 2차원 배열에 저장 */
    end;
  end;
run;

Hash object

구분 키워드 / 메서드 설명
선언 declare hash h, declare hiter hi("h") 해시 객체, 반복자 생성
주요 메서드 defineKey, defineData, defineDone, find, add, output key–data 구조 정의, 검색/추가/출력
Iterator first, next, last, prev key 순서로 순회
활용 Lookup table, 정렬, 순회 merge 대체, 빠른 검색, 정렬 처리

선언: DECLARE HASH, DECLARE HITER

  • Hash 객체 생성
declare hash h(dataset:"mydata");
  • Iterator 생성 (순회용)
declare hiter hi("h");
  • h는 해시 객체 이름, hi는 반복자(iterator)

주요 메서드: DEFINEKEY, DEFINEDATA, DEFINEDONE, FIND, ADD, OUTPUT

메서드 설명
defineKey 해시의 key 지정 (중복 불가)
defineData 저장할 데이터 변수 지정
defineDone 선언 종료 (필수)
find key 값으로 검색
add 새로운 key–data 쌍 추가
output 해시 객체 내용을 데이터셋으로 출력
if _N_=1 then do;
   declare hash h();
   h.defineKey("id");
   h.defineData("name","age");
   h.defineDone();
end;

rc = h.add();   /* 현재 PDV 값으로 추가 */
rc = h.find();  /* key 값으로 검색 */

Iterator: FIRST, NEXT, LAST, PREV

메서드 설명
first() 첫 번째 key로 이동
next() 다음 key로 이동
last() 마지막 key로 이동
prev() 이전 key로 이동
if _N_=1 then do;
   declare hiter hi("h");
end;

rc = hi.first();
do while (rc=0);
   put id= name= age=;
   rc = hi.next();
end;
  • hiter는 전체순환이라서 hi.first()처럼 지정해줘야 한다.

활용: Lookup table, 정렬, 순회

  • Lookup Table
    • 작은 데이터셋을 메모리에 로딩 후 key 기반 검색 (데이터 step merge 대신 고속 검색)
  • 정렬(Sort)
    • key 순서로 정렬된 순회 가능 (hiter.first(), hiter.next())
  • 순회(Iteration)
    • 전체 key–data 쌍을 순회하며 처리

hash 데이터셋 output하는 법

-

data example1;
    if _n_=1 then do;
        declare hash h(dataset:"sashelp.class");
        h.defineKey("name");             /* 키 */
        h.defineData("age","height","weight"); /* 데이터 */
        h.defineDone();
    end;
    set sashelp.class(keep=name);        /* 왼쪽 데이터 */
    rc = h.find();                       /* 키로 찾기 */
    if rc=0 then output;                 /* 매칭된 경우 출력 */
    drop rc;
run;
  • sashelp.class에서 name이 sashelp.class 데이터에서 만들어진 hash와 일치하면 output

-

data work.StateCityPopulation; 
    if _N_=1 then do; 
        if 0 then set pg3.population.usstates; 
        declare hash States(dataset:'pg3.population_usstates'); 
        States.definekey('StateName'); 
        States.definedata('Capital', 'StatePop2017'); 
        States.definedone(); 
        end; 
    set pg3.population uscities; 
    StateName=stnamel(StateCode): 
    RC=States. find(key:StateName); 
    *(해시 객체 States에서 주 이름(StateName)을 키로 검색해서 해당 Capital과 StatePop2017 값을 찾아서 자동으로 변수에 바인딩합니다 성공 시 RC = 0);
        if RC ne 0 then call missing(Capital, StatePop2017);
        *(해시에서 값을 찾지 못했을 경우, Capital과 StatePop2017 값을 명시적으로 결측 처리합니다.)
        PctPop«CityPop2017/StatePop2017; 
        format StatePop2017 comma14. PctPop percent8.1; 
        (해당하는 거만 데이터셋 만들어지게 하려면)
            if RC = 0 then do;  /* 🔍 lookup 성공한 경우에만 */
                PctPop = CityPop2017 / StatePop2017;
                format StatePop2017 comma14. PctPop percent8.1;
                output;           /* ✅ 해당 행만 데이터셋에 저장 */
        end;
    ()
run;

hiter

data test;
   input id name $ age;
   datalines;
1 Alice 14
2 Bob 15
3 Carol 13
;
run;

data _null_;
   if _N_ = 1 then do;
      declare hash h(dataset:"test");
      h.definekey("id");
      h.definedata("id","name","age");
      h.definedone();

      declare hiter hi("h");
   end;

   rc = hi.first();
   do while (rc = 0);
      putlog id= name= age=;
      rc = hi.next();
   end;
run;
  • hiter는 iterator로, 순회하면서 데이터 검색한다.
  • hi.first()로 첫번째 정해놓고,
  • do while로 rc가 0이 나올때까지 rc 검색
구분 find hiter
방식 key 값 직접 지정 모든 key 순회
반환 범위 단일 key 전체 key
사용 목적 lookup / join 전체 탐색 / 조건 필터링
output 가능? 가능 (if rc=0 then output;) 가능 (output; inside loop)

Utility Procedures

기능 문법/옵션 설명 예시
PROC FORMAT – PICTURE round, default=, prefix=, datatype=date 숫자/날짜 포맷 정의 $000,000,009, %Y-%0m-%0d
PROC FCMP – 함수 function … endsub; 사용자 정의 함수 function myadd(x,y) return(x+y);
PROC FCMP 호출 options cmplib=… 함수 등록 후 호출 x=myadd(10,20)

PROC FORMAT – PICTURE 문 (숫자/날짜 포맷 정의, 옵션: round, default, prefix 등)

  • PICTURE 문은 숫자/날짜를 원하는 출력 형태로 사용자 정의 포맷 가능.
  • VALUE와 달리 범주(label) 지정이 아니라, 숫자·날짜 표시 스타일을 직접 정의.
코드 설명 예시 값
%A 요일 이름 (전체) Wednesday
%a 요일 이름 (앞 3글자) Wed
%d 월 중 일자 (1~31, 한 자리 가능) 2
%0d 월 중 일자 (2자리, 0 채움) 02
%B 월 이름 (전체) January
%3B 월 이름 (앞 3글자) Jan
%m 월 번호 (1~12, 한 자리 가능) 1
%0m 월 번호 (2자리, 0 채움) 01
%Y 연도 (4자리) 2019
%y 연도 (2자리) 19
%H/%0H 시 (24시간제, 한 자리 또는 두 자리) 21
%I/%0I 시 (12시간제, 한 자리 또는 두 자리) 9 또는 09
%M/%0M 분 (한 자리 또는 두 자리) 13
%S/%0S 초 (한 자리 또는 두 자리) 5 또는 05
%p 오전/오후 표시 (AM/PM) PM
  • 기본
proc format;
   picture format-name
           low-high = 'template'
           (옵션들);
run;
  • template : 출력할 형태 정의 (9은 숫자 자리 표시, 0은 강제로 0 채움)
    • round → 반올림
    • default=n → 출력 자릿수 기본값 지정
    • prefix=‘문자’ → 출력값 앞에 문자 붙임
  • 숫자 포맷
proc format;
   picture myfmt
       low-high = '000,000,009' (prefix='$');
run;

data _null_;
   x=12345;
   put x=myfmt.;
run;
  • 결과

$000,012,345

  • 날짜 포맷 예시
proc format;
   picture mydate
       low-high = '%Y-%0m-%0d' (datatype=date);
run;

data _null_;
   x = '06SEP2025'd;
   put x=mydate.;
run;
  • 결과

2025-09-06

시험에서

proc format;
    picture MyDate (default=15)
    low-high = '%a-%d-%3B-¿y'
    (datatype=date) ;
    picture MyTime (default=14)
    low-high
    'H:%OH M:%OM S:%OS'
    (datatype=time) ;
run;

PROC FCMP – 사용자 정의 함수 (단일/다중 인자, 조건문 처리, CMPLIB= 옵션으로 호출)

  • 사용자 정의 함수(Function) 또는 서브루틴 작성 가능.

  • 만든 함수는 CMPLIB= 시스템 옵션으로 등록 후 호출 가능.

  • 기본

proc fcmp outlib=work.funcs.mycat;
   function myadd(x,y);
      return(x+y);
   endsub;
run;
  • outlib= : 라이브러리.저장데이터셋.카테고리

  • function : 사용자 정의 함수 선언

  • endsub; : 함수 종료

  • 다중 인자 + 조건문 처리

proc fcmp outlib=work.funcs.mycat;
   function mygrade(score);
      if score >= 90 then return('A');
      else if score >= 80 then return('B');
      else return('F');
   endsub;
run;
  • CMPLIB= 옵션으로 호출
options cmplib=work.funcs;

data _null_;
   x = myadd(10,20);
   g = mygrade(85);
   put x= g=;
run;
  • 결과

x=30 g=B

시험에서



PROC FCMP OUTLIB=libref.table.package;
FUNCTION function-name(arguments) <$> <length>;
...programming statements...
RETURN(expression);
ENDSUB;
RUN;
proc fcmp outlib-pg3. funcs.weather;
function FtoC (TempF) ;
TempC=round ( (TempF-32) *5/9,.01) ;
return (TempC) ;
endsub;
run;
*function불러와야 쓸 수 있음;
options cmplib=pg3.funcs;
  • 예시
    • 두 수 중 큰 값을 반환하는 함수 mymax를 정의하고 호출하시오.
proc fcmp outlib=work.funcs.math;
   function mymax(x,y);
      if x>y then return(x);
      else return(y);
   endsub;
run;

options cmplib=work.funcs;
data ex8;
   result=mymax(10,20);
   put result=;
run;

고급 함수

FINDC, FINDW, COUNT, COUNTC, COUNTW

함수 설명 예시 결과
FINDC 문자열에서 특정 문자(집합)의 위치 반환 findc("abc123","123") 4
FINDW 문자열에서 단어 위치 반환 (구분자 기반) findw("cat dog pig","dog"," ") 2
COUNT 특정 문자열이 포함된 횟수 (중첩 포함 가능) count("banana","ana") 2
COUNTC 특정 문자(집합)가 나타나는 횟수 countc("banana","a") 3
COUNTW 단어 개수 (구분자 기준) countw("cat dog pig") 3

LAG

시계열 데이터에서 이전 관측값 가져오기

lag1, lag2 있는 데이터 두 번 돌리면 아래처럼 이상하게 나타남(원래 데이터 10 20 30 40)

큐를 초기화하거나 lag 대신 retain+by 등으로 대체 고려

data test;
   input x;
   lag1_x = lag1(x);
   lag2_x = lag2(x);
   datalines;
10
20
30
40
;
run;

proc print data=test; run;
  • 결과
x lag1_x lag2_x
10 . .
20 10 .
30 20 10
40 30 20

Regex 함수 (PRX)

메타문자: () [] {} * + ? . | ^ $

패턴 설명
. 아무 문자 하나 (줄바꿈 제외)
\d 숫자 (digit, 0-9)
\w 알파벳/숫자/언더스코어
\s 공백 문자 (space, tab 등)
^ 문자열의 시작
$ 문자열의 끝
+ 1개 이상 반복
* 0개 이상 반복
? 0개 또는 1개
[abc] a, b, 또는 c 중 하나
[^abc] a, b, c 이외의 문자
(abc) 그룹

함수: PRXMATCH, PRXPARSE, PRXCHANGE

함수 설명 예시 결과
PRXPARSE 정규식 패턴을 컴파일 (패턴 ID 반환) pattern=prxparse('/cat/'); 패턴 ID
PRXMATCH 문자열에서 정규식 매칭 위치 반환 prxmatch('/dog/',"my dog") 4
PRXCHANGE 정규식 기반 치환 prxchange('s/dog/cat/',-1,"dog dog") cat cat

시험에서

  1. prxparse → 정규식 패턴 컴파일
  • prxparse(‘//’) → “숫자 1개 이상” 정규식 생성
  • “abc123xyz”에서 숫자는 4번째 위치 → pos=4
data _null_;
   retain regex;
   if _n_ = 1 then regex = prxparse('/\d+/');  /* 숫자 찾기 */
   str = "abc123xyz";
   pos = prxmatch(regex, str);
   put pos=;
run;
  • = 같이 입력하면 pos = 값으로 출력
  1. prxmatch → 문자열에 패턴 매칭 (첫 번째 위치)
  • “cat”은 “my cat is here”에서 4번째 → pos1=4
  • “dog only”에는 없음 → pos2=0
data _null_;
   regex = prxparse('/cat/');
   str1 = "my cat is here";
   str2 = "dog only";
   pos1 = prxmatch(regex, str1);
   pos2 = prxmatch(regex, str2);
   put pos1= pos2=;
run;
  1. prxchange → 정규식 치환
  • 정규식 ()-()-() → 연도-월-일 캡처
  • $3/$2/$1 → 일/월/연도로 재배치
  • 결과: 17/08/2025
data _null_;
   str = "2025-08-17";
   /* yyyy-mm-dd → dd/mm/yyyy 변환 */
   new = prxchange('s/(\d{4})-(\d{2})-(\d{2})/$3\/$2\/$1/', -1, str);
   put new=;
run;
  1. prxsubstr → 매치 위치와 길이 반환
  • 첫 숫자 시작 = 14번째 (pos=14)
  • 길이 = 5 (len=5)
  • matched=12345
data _null_;
   regex = prxparse('/\d+/');  /* 숫자 찾기 */
   str = "Order number 12345 confirmed";
   call prxsubstr(regex, str, pos, len);
   matched = substr(str, pos, len);
   put pos= len= matched=;
run;
  1. prxnext → 여러 매치 순회
  • prxnext는 여러 개의 매치를 순차적으로 반환할 때 사용.
data _null_;
   regex = prxparse('/\d+/');  /* 숫자 찾기 */
   str = "a1 b22 c333";
   start = 1;
   stop = length(str);

   do while (prxnext(regex, start, stop, str, pos, len) > 0);
      match = substr(str, pos, len);
      put match=;
   end;
run;