Cursor : 쿼리 결과를 가리키는 포인터 / 쿼리 결과를 잠깐 동안 저장하고 있는 변수라고 생각하면 된다.
- 암시적커서
- 명시적커서
(1) Implicit cursors (암시적 커서)
- SELECT INTO, INSERT, UPDATE, DELETE는 implicit cursor을 생성
- cursor는 SQL%ROWCOUNT, SQL%ISOPEN, SQL%FOUND, SQL%NOTFOUND의 status를 가지고 있음
- 만일 query가 zero or multiple row일 경우 exception을 보여줌
(2) Explicit cursors (명시적 커서)
- OPEN, FETCH, CLOSE의 execution cycle을 가지고 있음.
사실 잘 이해는 안간다. 예시를 통해 이해해보도록 하자. 커서의 기본 구조는 아래와 같다.
CURSOR cursor_name IS query;
커서를 사용하기 위해 커서를 열어준다.
OPEN cursor_name;
FETCH는 row를 cursor에 저장해준다.
FETCH cursor_name INTO variable_list;
fetching이 끝나면 커서를 닫아준다.
CLOSE cursor_name;
함수나 블록 구간의 커서는 끝나면 자동으로 닫히고, 필요하다면 임의로 닫아줄 수도 있다. 하지만 패키지 기반 커서는 직접 닫아주어야 한다.
커서는 4개의 attribute를 가지고 있으며 attribute format이다.
cursor_name%attribute
1) %ISOPEN
- 둘 중에 하나 딱 고름 TRUE, FALSE
2) %FOUND
- NULL (before the first fetch)
- TRUE (fetch 다 함)
- FALSE (return되는 row가 없을 때)
- INVALID_CURSOR (cursor 안열림)
3) %NOTFOUND
- NULL (before the first fetch)
- FALSE (fetch 다함)
- TURE ( row X)
- INVALID_CURSOR (안열림)
4) %ROWCOUNT
- returns the number of rows fetched (fetch된 row의 개수를 리턴)
예를 들어서 이해해보자.
마찬가지로 example table이 있다.
CREATE VIEW sales AS
SELECT customer_id,
SUM(unit_price * quantity) total,
ROUND(SUM(unit_price * quantity) * 0.05) credit
FROM order_items
INNER JOIN orders USING (order_id)
WHERE status = 'Shipped'
GROUP BY customer_id;
sales 라는 view를 생성한다. 만일 권한 부족으로 생성되지 않으면 관리자 계정으로 로그인 이후 권한을 쥐어준다.
sqlplus "as /sysdba"
SQL> grant create view to scott;
Grant succeeded.
sales view는 무슨 정보를 담고 있을까? customer_id로 그룹을 지어서 status가 shipped인 애들만 뽑아주었다. 이후 custmer_id(건당 그룹 지어진), total(가격 칭구칭구의 sum 값), round(위에꺼 * 0.05 -> 매출의 5%) credit이다.
다음과 같은 상황이 주어졌다고 생각해본다.
- 모든 고객의 신용한도를 0으로 초기화한다.
- sales를 내림차순으로 정렬하여 백만원의 예산에서 새로운 한도를 만든다.
DECLARE
n_budget NUMBER := 1000000;
-- cursor
CURSOR cur_sales IS
SELECT * FROM sales
ORDER BY total DESC;
-- record
r_sales cur_sales%ROWTYPE;
BEGIN
-- reset credit limit of all customers
UPDATE customers SET credit_limit = 0;
OPEN cur_sales;
LOOP
FETCH cur_sales INTO r_sales;
EXIT WHEN cur_sales%NOTFOUND;
-- update credit for the current customer
UPDATE
customers
SET
credit_limit =
CASE WHEN n_budget > r_sales.credit
THEN r_sales.credit
ELSE n_budget
END
WHERE
customer_id = r_sales.customer_id;
-- reduce the budget for credit limit
n_budget := n_budget - r_sales.credit;
DBMS_OUTPUT.PUT_LINE( 'Customer id: ' ||r_sales.customer_id ||
' Credit: ' || r_sales.credit || ' Remaining Budget: ' || n_budget );
-- check the budget
EXIT WHEN n_budget <= 0;
END LOOP;
CLOSE cur_sales;
END;
익명 블록이 완료되었습니다.
Customer id: 47 Credit: 155419 Remaining Budget: 844581
Customer id: 49 Credit: 122625 Remaining Budget: 721956
Customer id: 1 Credit: 120304 Remaining Budget: 601652
Customer id: 48 Credit: 110282 Remaining Budget: 491370
Customer id: 44 Credit: 97908 Remaining Budget: 393462
Customer id: 18 Credit: 89511 Remaining Budget: 303951
Customer id: 46 Credit: 80418 Remaining Budget: 223533
Customer id: 6 Credit: 62224 Remaining Budget: 161309
Customer id: 17 Credit: 60233 Remaining Budget: 101076
Customer id: 16 Credit: 59579 Remaining Budget: 41497
Customer id: 45 Credit: 56295 Remaining Budget: -14798
계산된 total만큼 budget에서 빼고 0원 이하가 되면 끝난다.
한번 credit limit가 바뀌었는지 살펴보자.
SELECT customer_id,
name,
credit_limit
FROM customers
ORDER BY credit_limit DESC;
limit의 값이 0인 항목들도 있다. 알고리즘이 잘못된 것인지 확인하려면 SUM을 사용하면 된다.
SELECT
SUM( credit_limit )
FROM
customers;
'Database > PL SQL' 카테고리의 다른 글
PL/SQL Tutorial - Parameters (0) | 2019.05.08 |
---|---|
PL/SQL Tutorial - Cursor FOR LOOP (0) | 2019.05.08 |
PL/SQL Tutorial - SELECT INTO (0) | 2019.05.08 |
PL/SQL Tutorial - Exception handlers (0) | 2019.05.08 |
PL/SQL Tutorial - CONTINUE (0) | 2019.05.08 |