KoreanFoodie's Study
SQL 기초 - 15. PL/SQL 제어문 (IF, LOOP...), 예외처리(EXCEPTION, RAISE) 본문
Database
SQL 기초 - 15. PL/SQL 제어문 (IF, LOOP...), 예외처리(EXCEPTION, RAISE)
GoldGiver 2021. 10. 25. 09:26
이 글은 '김상형의 SQL 정복'의 예제 코드를 설명하는 방식으로 SQL 쿼리 예제를 소개하고 있습니다.
SQL 기초 - 15. PL/SQL 제어문 (IF, LOOP...), 예외처리(EXCEPTION, RAISE)
-- 다음 선언문으로 출력문을 쓰겠다는 선언부터 해야 한다.
-- DBMS_OUTPUT.PUT_LINE(출력할 내용) 을 사용할 수 있게 함
SET SERVEROUTPUT ON;
BEGIN
DBMS_OUTPUT.PUT_LINE('안녕하세요');
END;
BEGIN
DBMS_OUTPUT.PUT_LINE('안녕하세요')
END
BEGIN
DBMS_OUTPUT.PUT_LINE(2 + 3 * 4);
DBMS_OUTPUT.PUT_LINE(POWER(2, 3));
END;
-- DECLARE 문 안에 변수를 선언한다. 변수 선언은 := 을 사용한다.
DECLARE
v_price INT := 1000;
v_num INT := 5;
v_total INT;
BEGIN
v_total := v_price * v_num;
DBMS_OUTPUT.PUT_LINE(v_total);
END;
DECLARE v_price INT := 1000;v_num INT := 5;v_total INT;
BEGIN v_total := v_price * v_num;DBMS_OUTPUT.PUT_LINE(v_total);END;
-- CONSTANT로 상수 선언이 가능하다.
DECLARE
c_mile CONSTANT NUMBER := 1.609;
v_kilo INT;
BEGIN
v_kilo := 400;
DBMS_OUTPUT.PUT_LINE(v_kilo * c_mile);
END;
DECLARE
v_price tItem.price%TYPE := 1000;
v_num v_price%TYPE := 5;
v_total v_price%TYPE;
BEGIN
v_total := v_price * v_num;
DBMS_OUTPUT.PUT_LINE(v_total);
END;
-- 오라클에는 BOOLEAN 타입이 없지만, 대신 (1 = 1)로 표현할 수 있다.
DECLARE
v_isOk BOOLEAN;
BEGIN
v_isOk := (1 = 1);
IF v_isOK THEN
DBMS_OUTPUT.PUT_LINE('OK');
END IF;
END;
-- 테이블 안에 있는 데이터 타입을 그대로 가져오려면 %TYPE을 사용한다.
-- SELECT 필드 INTO 변수 FROM을 사용하면 테이블에 있는 값을 대입할 수 있다.
DECLARE
v_member tOrder.member%TYPE;
BEGIN
SELECT member INTO v_member FROM tOrder WHERE orderID = 1;
DBMS_OUTPUT.PUT_LINE(v_member);
END;
DECLARE
v_member tOrder.member%TYPE;
v_item VARCHAR(20);
BEGIN
SELECT member, item INTO v_member, v_item FROM tOrder WHERE orderID = 1;
DBMS_OUTPUT.PUT_LINE(v_member || '의 ' || v_item || ' 주문');
END;
-- 전체 ROW를 가져오고 싶을 때는 %ROWTYPE을 사용한다.
DECLARE
v_row tCity%ROWTYPE;
BEGIN
SELECT * INTO v_row FROM tCity WHERE name = '부산';
DBMS_OUTPUT.PUT_LINE(TRIM(v_row.name) || ', ' || v_row.area || ', ' || v_row.popu);
END;
DECLARE
v_row tCity%ROWTYPE;
BEGIN
SELECT name, region INTO v_row.name, v_row.region FROM tCity WHERE name = '춘천';
DBMS_OUTPUT.PUT_LINE(v_row.region || '도의 ' || v_row.name);
END;
-- 레코드 타입은 복수 개의 열로 구성된 하나의 레코드를 표현한다.
-- 사용자가 원하는 열을 묶어 레코드 타입을 선언할 수 있다. (여기서는 cap)
DECLARE
TYPE cap IS RECORD(area INT, popu INT);
city cap;
BEGIN
SELECT area, popu INTO city FROM tCity WHERE name = '부산';
DBMS_OUTPUT.PUT_LINE(city.area || ',' || city.popu);
END;
TYPE cap IS RECORD(area tCity.area%TYPE, popu tCity.popu%TYPE);
DECLARE
v_maxPopu INT;
v_cityName VARCHAR(10);
BEGIN
SELECT MAX(popu) INTO v_maxPopu FROM tCity;
SELECT name INTO v_cityName FROM tCity WHERE popu = v_maxPopu;
DBMS_OUTPUT.PUT_LINE(v_cityName);
END;
DECLARE
v_popu INT;
BEGIN
SELECT popu INTO v_popu FROM tCity WHERE name = '서울';
v_popu := v_popu * 2;
UPDATE tCity SET popu = v_popu WHERE name = '서울';
END;
-- 컬렉션 : VARRAY로 배열을 표현할 수 있다.
DECLARE
TYPE int_array IS VARRAY(5) OF NUMBER;
ar int_array;
BEGIN
ar := int_array(8, 9, 0, 6, 2);
FOR idx IN 1..5
LOOP
DBMS_OUTPUT.PUT_LINE(ar(idx));
END LOOP;
END;
-- 이중 배열도 가능하다.
DECLARE
TYPE int_array IS VARRAY(3) OF NUMBER;
TYPE int_array2 IS VARRAY(3) OF int_array;
ar int_array2;
BEGIN
ar := int_array2(int_array(1, 2, 3), int_array(4, 5, 6), int_array(7, 8, 9));
DBMS_OUTPUT.PUT_LINE(ar(2)(3));
END;
-- 중첩 테이블(Nested Table)은 크기를 미리 밝히지 않는 배열이다.
-- 생성자에서 필요한 만큼 배열 요소를 나열, 생성할 때 배열 크기를 결정한다.
DECLARE
TYPE int_table IS TABLE OF NUMBER;
ar int_table;
BEGIN
ar := int_table(1, 2, 3, 4, 5, 6);
DBMS_OUTPUT.PUT_LINE(ar(4));
END;
-- 연관 배열은 C++의 맵이나 Python의 사전과 같다.
-- 삭제는 DELETE, 존재 여부 조사는 EXIST, 요소 개수 조사는 COUNT이다.
DECLARE
TYPE string_int_map IS TABLE OF NUMBER INDEX BY STRING(20);
score string_int_map;
BEGIN
score('홍길동') := 80;
score('황진이') := 90;
score('어우동') := 90;
score.DELETE('황진이');
DBMS_OUTPUT.PUT_LINE(score.COUNT());
DBMS_OUTPUT.PUT_LINE(score('홍길동'));
END;
DECLARE v_score INT := 12;
BEGIN
IF v_score = 12 THEN
DBMS_OUTPUT.PUT_LINE('12입니다');
ELSE
DBMS_OUTPUT.PUT_LINE('12가 아닙니다');
END IF;
END;
DECLARE v_score INT := 12;
BEGIN
IF v_score = 12 THEN
DBMS_OUTPUT.PUT_LINE('12입니다');
DBMS_OUTPUT.PUT_LINE('참 잘했어요.');
ELSE
DBMS_OUTPUT.PUT_LINE('12가 아닙니다');
DBMS_OUTPUT.PUT_LINE('좀 더 노력하세요.');
END IF;
END;
DECLARE
v_popu INT;
v_message VARCHAR(50);
BEGIN
SELECT popu INTO v_popu FROM tCity WHERE name = '부산';
IF v_popu > 100 THEN
v_message := '100만이 넘습니다';
ELSE
v_message := '100만보다 적습니다';
END IF;
DBMS_OUTPUT.PUT_LINE('부산의 인구는 ' || v_message);
END;
DECLARE
v_num INT := 1;
v_total INT := 0;
BEGIN
WHILE v_num <= 100
LOOP
v_total := v_total + v_num;
v_num := v_num + 1;
END LOOP;
DBMS_OUTPUT.PUT_LINE('1~100까지의 합산 결과 = ' || v_total);
END;
DECLARE
v_total INT := 0;
BEGIN
FOR v_num IN 1 .. 100
LOOP
v_total := v_total + v_num;
END LOOP;
DBMS_OUTPUT.PUT_LINE('1~100까지의 합산 결과 = ' || v_total);
END;
BEGIN
FOR v_cities IN (SELECT * FROM tCity WHERE region='경기')
LOOP
DBMS_OUTPUT.PUT_LINE(TRIM(v_cities.name) || ' : ' || v_cities.area || ',' || v_cities.popu);
END LOOP;
END;
-- EXIT문으로 WHILE 루프를 탈출할 수 있다.
DECLARE
v_num INT := 1;
v_total INT := 0;
BEGIN
WHILE 1 = 1
LOOP
v_total := v_total + v_num;
v_num := v_num + 1;
IF v_num > 100 THEN
EXIT;
END IF;
END LOOP;
DBMS_OUTPUT.PUT_LINE('1~100까지의 합산 결과 = ' || v_total);
END;
-- EXIT WHEN .. 을 사용하면 더 간략히 표현이 가능하다.
DECLARE
v_num INT := 1;
v_total INT := 0;
BEGIN
LOOP
v_total := v_total + v_num;
v_num := v_num + 1;
EXIT WHEN v_num > 100;
END LOOP;
DBMS_OUTPUT.PUT_LINE('1~100까지의 합산 결과 = ' || v_total);
END;
-- GOTO 문법은 잘 사용하지 않는다.
DECLARE
v_num INT := 1;
v_total INT := 0;
BEGIN
<<HERE>>
v_total := v_total + v_num;
v_num := v_num + 1;
IF v_num <= 100 THEN
GOTO HERE;
END IF;
DBMS_OUTPUT.PUT_LINE('1~100까지의 합산 결과 = ' || v_total);
END;
-- DBMS_LOCK.SLEEP 명령은 지정한 초 동안 아무것도 하지 않고 대기한다.
-- 실수 지정도 가능하다.
BEGIN
DBMS_LOCK.SLEEP(5);
DBMS_OUTPUT.PUT_LINE('대기종료');
END;
DECLARE
v_num INT := 2;
v_numword CHAR(30);
BEGIN
v_numword :=
CASE v_num
WHEN 1 THEN '하나'
WHEN 2 THEN '둘'
WHEN 3 THEN '셋'
WHEN 4 THEN '넷'
ELSE '그외'
END;
DBMS_OUTPUT.PUT_LINE(v_numword);
END;
DECLARE
v_num INT := 6;
v_numword CHAR(30);
BEGIN
v_numword :=
CASE
WHEN v_num < 0 THEN '음수'
WHEN v_num IN (1, 2, 3) THEN '하나, 둘, 셋'
WHEN v_num > 4 THEN '넷보다 더 큼'
ELSE '그외'
END;
DBMS_OUTPUT.PUT_LINE(v_numword);
END;
SELECT item,
CASE category
WHEN '패션' THEN '받자 마자 빨아서 입으세요.'
WHEN '가전' THEN '충격을 주지 마세요.'
WHEN '식품' THEN '냉장 보관하세요.'
WHEN '성인' THEN '애들은 가라'
END AS 주의사항
FROM tItem
SELECT member, item,
CASE status
WHEN 1 THEN '주문'
WHEN 2 THEN '배송중'
WHEN 3 THEN '배송완료'
ELSE '기타'
END AS 상태
FROM tOrder;
-- 오라클에서는 간단한 CASE문을 DECODE를 이용해서 표현할 수도 있다.
SELECT member, item, DECODE(status, 1, '주문', 2, '배송중', 3, '배송완료', '기타')
AS 상태 FROM tOrder;
DECLARE
v_num INT := 2;
v_popu INT;
BEGIN
CASE
WHEN v_num = 1 THEN SELECT popu INTO v_popu FROM tCity WHERE name = '서울';
WHEN v_num = 2 THEN DBMS_OUTPUT.PUT_LINE('둘');
WHEN v_num = 3 THEN COMMIT;
WHEN v_num > 4 THEN ROLLBACK;
ELSE DBMS_OUTPUT.PUT_LINE('알 수 없는 명령');
END CASE;
END;
오라클 예외처리 목록 :
DECLARE v_member CHAR(20);
BEGIN
SELECT member INTO v_member FROM tOrder WHERE orderID = 100;
DBMS_OUTPUT.PUT_LINE(v_member);
END;
DECLARE v_member CHAR(20);
BEGIN
SELECT member INTO v_member FROM tOrder WHERE orderID = 100;
DBMS_OUTPUT.PUT_LINE(v_member);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('주문 번호가 없습니다.');
END;
DECLARE v_member CHAR(20);
BEGIN
SELECT member INTO v_member FROM tOrder WHERE orderID > 1;
DBMS_OUTPUT.PUT_LINE(v_member);
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('하나의 주문만 선택하십시오.');
END;
-- RAISE로 사용자 설정 예외를 만들 수 있다!
DECLARE
v_member CHAR(20);
v_orderID INT := -1;
negativeOrder EXCEPTION;
BEGIN
IF (v_orderID < 0) THEN
RAISE negativeOrder;
END IF;
SELECT member INTO v_member FROM tOrder WHERE orderID = v_orderID;
DBMS_OUTPUT.PUT_LINE(v_member);
EXCEPTION
WHEN negativeOrder THEN
DBMS_OUTPUT.PUT_LINE('주문 번호가 음수여서는 안됩니다.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('원인을 알 수 없는 예외가 발생했습니다.');
END;
DECLARE
v_member CHAR(20);
v_orderID INT := -1;
BEGIN
IF (v_orderID < 0) THEN
RAISE_APPLICATION_ERROR(-20000, '주문 번호가 음수여서는 안됩니다.');
END IF;
SELECT member INTO v_member FROM tOrder WHERE orderID = v_orderID;
DBMS_OUTPUT.PUT_LINE(v_member);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
-- 아래는 SQL 서버의 제어문이다. 필요한 사람만 참고로 하자.
SELECT '안녕하세요';
SELECT 2 + 3 * 4;
SELECT POWER(2, 3);
DECLARE @salary INT; -- 정수형의 @salary 변수
DECLARE @a INT, @b INT = 100; -- 두 개의 정수형 변수. b는 100으로 초기화
DECLARE @age INT, @name CHAR(5); -- 정수형 변수와 문자열 변수 선언
DECLARE @price INT;
SET @price = 1000;
SELECT @price;
DECLARE @name CHAR(20);
SELECT @name = member FROM tOrder WHERE orderID=1;
SELECT @name;
SET @name = (SELECT member FROM tOrder WHERE orderID=1);
DECLARE @score INT = 12;
IF @score = 12
PRINT '12입니다.';
ELSE
PRINT '12가 아닙니다.';
DECLARE @message VARCHAR(50);
IF (SELECT popu FROM tCity WHERE name = '부산') > 100
BEGIN
SET @message='100만이 넘습니다';
PRINT '많이도 낳았네요'
END
ELSE
BEGIN
SET @message='100만보다 적습니다';
PRINT '아직 더 많이 낳아야겠네요'
END
PRINT '부산의 인구는 ' + @message;
DECLARE @num INT = 1, @sum INT = 0;
WHILE @num <= 100
BEGIN
SET @sum = @sum + @num;
SET @num = @num + 1;
END
PRINT '1~100까지의 합산 결과 = ' + CONVERT(VARCHAR(10), @sum);
WHILE 1=1
BEGIN
SET @sum = @sum + @num;
SET @num = @num + 1;
IF @num > 100
BREAK;
END
HERE:
SET @sum = @sum + @num;
SET @num = @num + 1;
IF @num <= 100
GOTO HERE;
WAITFOR DELAY '00:00:05'; -- 5초간 대기
WAITFOR TIME '02:20:00'; -- 2:20분까지 대기
BEGIN TRY
INSERT INTO tCity VALUES ('서울',600,1000,'y','경기');
END TRY
BEGIN CATCH
PRINT '기본키가 같은 레코드를 삽입할 수 없습니다.';
PRINT '에러 번호 : ' + CAST(ERROR_NUMBER() AS VARCHAR(10))
PRINT '메시지 : ' + ERROR_MESSAGE()
END CATCH
BEGIN TRY
SELECT mayor FROM tCity;
END TRY
BEGIN CATCH
PRINT '시장님에 대한 정보는 없습니다.'
END CATCH
DECLARE @popu INT = 12345;
BEGIN TRY
IF @popu > 10000
THROW 50001, '도시의 인구가 1억이 넘을 리가 없을텐데...', 1;
ELSE
INSERT INTO tCity VALUES ('광주',600,@popu,'y','전라');
END TRY
BEGIN CATCH
PRINT '에러 번호 : ' + CAST(ERROR_NUMBER() AS VARCHAR(10))
PRINT '메시지 : ' + ERROR_MESSAGE()
END CATCH
'Database' 카테고리의 다른 글
SQL 기초 - 17. 커서와 동적쿼리 (0) | 2021.10.25 |
---|---|
SQL 기초 - 16. 저장 프로시저, 인수, 함수(+테이블 반환 함수) (0) | 2021.10.25 |
SQL 기초 - 14. 테이블 관리 (테이블 수정, 필드 수정, SQL ALTER) (0) | 2021.10.21 |
SQL 기초 - 13. 분석과 통계(소계, 순위, 통계, 피봇) (0) | 2021.10.21 |
SQL 기초 - 12. 뷰 (임시 테이블, CTE) (0) | 2021.10.20 |
Comments