[Oracle] PL/SQL
Oracle pipeline을 개발하다 보니 INSERT, UPDATE 등 익숙한 opernation외에 PL/SQL
이라는 것을 발견해서 찾아보게 되었다.
mysql에서는 이와 비슷한 ‘SQL/PSM’ 이 있고 MsSQL의 트랜잭션과 비슷하지만 PL/SQL은 실제 프로그래밍 언어처럼 사용할 수 있다는 점에서 차이가 있다.
PL/SQL?
PL/SQL은 Oracle에서 사용되는 절차적 언어로, SQL을 확장해서 더 다양한 기능을 제공한다.
원래 SQL은 선언적 언어기 때문에 데이터를 어떻게 얻을지 기술하는 방식이었지만, PL/SQL은 절차적 언어이기 때문에 변수,조건문, 반복문 등을 이용해서 데이터를 어떻게 처리할지 정의할 수 있다. 그리고 선언부, 실행부, 예외처리부 와 같이 명확히 분리된 블럭 구조로 되어 있다.
PL/SQL 구조
DECLARE
: 선언부, 사용되는 변수나 상수를 선언하는 부분BEGIN
: 실행부, 절차적 형식으로 SQL문을 실행할 수 있도록 제어문,반복문, 함수 등을 정의하는 부분EXCEPTION
: 예외 처리부, PL/SQL 문이 실행되는 중에 발생할 수 있는 에러의 예외처리를 기술하는 부분- ‘END’ : PL/SQL 종료
변수 선언 방법
1
2
DECLARE
변수명 데이터타입 [:= 초기값];
아래는 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 단순 변수 선언
DECLARE
emp_count NUMBER;
-- 초기 값 할당
DECLARE
emp_name VARCHAR2(100) := 'John Doe';
/* 복잡한 데이터 타입
start_date는 날짜 유형으로 오늘 날짜로 초기화되며, total_sales는 소수점 두 자리까지 표현 가능한 숫자 유형
*/
DECLARE
start_date DATE := SYSDATE;
total_sales NUMBER(10,2);
- 변수명은 문자로 시작해야하고 특수문자와 공백 포함 X
- 대소문자를 구분하지 않지만 변수명은 주로 소문자나 스네이크 케이스로 작성
SELECT INTO
변수에 :=
로 단일 값을 대입할 수도 있지만 SELECT INTO 문을 이용할 수도 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
SELECT 컬럼명
INTO 변수명
FROM 테이블명
WHERE 조건;
/* 단일 컬럼 값 대입
employee_id가 101인 직원의 salary를 조회하여 emp_salary 변수에 저장
*/
DECLARE
emp_salary NUMBER;
BEGIN
SELECT salary INTO emp_salary FROM employees WHERE employee_id = 101;
DBMS_OUTPUT.PUT_LINE('Employee salary: ' || emp_salary);
END;
/* 여러 컬럼 값 대입
employee_id가 102인 직원의 이름(first_name)과 부서명(department_name)을 조회하여 각각 emp_name, emp_department 변수에 저장
*/
DECLARE
emp_name VARCHAR2(100);
emp_department VARCHAR2(50);
BEGIN
SELECT first_name, department_name
INTO emp_name, emp_department
FROM employees JOIN departments USING (department_id)
WHERE employee_id = 102;
DBMS_OUTPUT.PUT_LINE('Employee: ' || emp_name || ', Department: ' || emp_department);
END;
SELECT INTO 문은 정확히 하나의 행을 반환해야 하고 그렇지 않으면 에러 발생
PL/SQL 종류
익명 블록
말 그대로 이름 없는 PL/SQL 문으로 일회성 스크립트나 테스트 목적으로 사용된다.
1
2
3
4
5
6
7
8
9
10
11
DECLARE
emp_count NUMBER;
BEGIN
SELECT COUNT(*) INTO emp_count FROM employees WHERE department_id = 10;
DBMS_OUTPUT.PUT_LINE('Department 10 has ' || emp_count || ' employees.');
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No employees found in Department 10.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('Error occurred: ' || SQLERRM);
END;
부서 10의 직우너 수를 계산하고 결과를 출력하는 익명 블록이고 데이터를 찾을 수 없거나 다른 오류에 대한 예외처리도 포함되어 있다.
저장 프로시저
DB에 저장되며, 입력 매개변수를 받을 수 있고 리턴 값을 하나 이상 가질 수 있는 프로그램
1
2
3
4
5
6
7
CREATE OR REPLACE PROCEDURE UpdateSalary(emp_id IN NUMBER, new_salary IN NUMBER) IS
BEGIN
UPDATE employees SET salary = new_salary WHERE employee_id = emp_id;
IF SQL%ROWCOUNT = 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'Employee not found');
END IF;
END UpdateSalary;
이 프로시저는 특정 직원의 급여를 업데이트하고, 오류가 있을 시에 사용자 정의 오류를 발생시킨다.
함수
저장 프로시저와 유사하지만 항상 값을 반환해야하고 다른 SQL문 내에서 호출 가능
1
2
3
4
5
6
7
8
9
10
CREATE OR REPLACE FUNCTION GetEmployeeName(emp_id IN NUMBER) RETURN VARCHAR2 IS
emp_name VARCHAR2(100);
BEGIN
SELECT first_name || ' ' || last_name INTO emp_name FROM employees WHERE employee_id = emp_id;
RETURN emp_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END GetEmployeeName;
이 함수는 주어진 직원 ID에 해당하는 직원의 이름을 반환하고 존재하지 않을 경우 NULL 반환
트리거
데이터베이스 테이블 변화를 주는 특정 이벤트(INSERT,UPDATE 등)에 자동으로 반응해서 실행되는 PL/SQL
1
2
3
4
5
6
7
8
CREATE OR REPLACE TRIGGER LogEmployeeUpdate
AFTER UPDATE ON employees
FOR EACH ROW
BEGIN
INSERT INTO employee_audit (employee_id, audit_date)
VALUES (:NEW.employee_id, SYSDATE);
END LogEmployeeUpdate;
이 트리거는 employees 테이블에 대한 갱신 후 각 행에 대해 감사 로그를 기록한다.
패키지
관련 프로시저, 함수, 변수, 타입 등을 그룹화한 컨테이너, 복잡한 프로그램을 모둘화하고 재사용 가능하게 해준다.
저장 프로시저와 함수는 비슷해보이지만 주요 차이점은 아래와 같다.
- 저장 프로시저는 데이터 삽입, 삭제, 업데이트 등 복잡한 비즈니스 로직을 처리하고 함수는 계산을 수행하고 단일 값을 반환하는데 사용된다.
- 저장 프로시저는 리턴 값이 있어도 되고 없어도 되지만, 함수는 반드시 리턴 값을 반환해야 한다.
- 저장 프로시저는 독립적으로 호출되고, 함수는 SQL 쿼리 내에서 다른 함수와 함께 사용될수 있다.