PL SQL 2 - ynjch97/YNJCH_WIKI GitHub Wiki

1. μ»€μ„œ

  • Oracle μ„œλ²„μ—μ„œ ν• λ‹Ήν•œ μ „μš© λ©”λͺ¨λ¦¬ μ˜μ—­μ— λŒ€ν•œ 포인터
  • SQL문을 μ²˜λ¦¬ν•œ κ²°κ³Ό 집합을 κ°€λ¦¬ν‚€λŠ” 포인터
  • 질의 결과둜 μ–»μ–΄μ§„ μ—¬λŸ¬ 행이 μ €μž₯된 λ©”λͺ¨λ¦¬ μƒμ˜ μœ„μΉ˜
    • Oracle μ„œλ²„ ν”„λ‘œμ„ΈμŠ€ λ‚΄λΆ€μ˜ Private SQL Area λΌλŠ” λ©”λͺ¨λ¦¬ μ˜μ—­μ— κ²°κ³Ό 집합이 μ €μž₯됨
    • Private SQL Area : νŠΉμ • 쿼리에 λŒ€ν•œ κ²°κ³Όλ₯Ό μ €μž₯ν•˜κ³  μΊμ‹±ν•˜λŠ” μ—­ν• 
  • ν•œ μ„Έμ…˜ μ•ˆμ—μ„œ 같은 쿼리λ₯Ό 반볡 ν˜ΈμΆœν•˜λ©΄ μ €μž₯λ˜μ–΄ μžˆλŠ” κ²°κ³Ό 집합을 λ°˜ν™˜ > μžμ› λ‚­λΉ„ μ΅œμ†Œν™”
  • SELECT문의 κ²°κ³Ό 집합을 μ²˜λ¦¬ν•˜λŠ”λ° μ‚¬μš©

1-1. μ•”μ‹œμ  μ»€μ„œ(Implict Cursor)

  • λ‚΄λΆ€μ—μ„œ μžλ™μœΌλ‘œ μƒμ„±λ˜μ–΄ μ‚¬μš©
  • Oracle DBμ—μ„œ μ‹€ν–‰λ˜λŠ” λͺ¨λ“  SQLλ¬Έμž₯은 μ•”μ‹œμ  μ»€μ„œκ°€ μƒμ„±λ˜λ©°, μ»€μ„œ 속성을 μ‚¬μš© κ°€λŠ₯
  • Oracle μ„œλ²„μ—μ„œ SQL문을 μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ λ‚΄λΆ€μ μœΌλ‘œ 생성 및 관리함
  • λͺ¨λ“  DMLκ³Ό PL/SQL SELECT문에 λŒ€ν•΄ 선언됨
  • PL/SQL 블둝 μ•ˆμ—μ„œ μ‹€ν–‰ν•˜λŠ” SQL λ¬Έμž₯ μ‹€ν–‰ μ‹œ μžλ™μœΌλ‘œ λ§Œλ“€μ–΄μ Έ 생성
  • SQL문이 μ‹€ν–‰λ˜λŠ” μˆœκ°„ μžλ™μœΌλ‘œ OPEN, CLOSEλ₯Ό μ‹€ν–‰
  • SQL μ»€μ„œ 속성을 μ‚¬μš©ν•˜λ©΄ SQL문의 κ²°κ³Όλ₯Ό ν…ŒμŠ€νŠΈν•  수 있음

1-1-1. μ•”μ‹œμ  μ»€μ„œ 속성

  • SQL%FOUND : ν•΄λ‹Ή SQL문에 μ˜ν•΄ λ°˜ν™˜λœ ν–‰μˆ˜κ°€ 1개 이상이면 TRUE
  • SQL%NOTFOUND : ν•΄λ‹Ή SQL문에 μ˜ν•΄ λ°˜ν™˜λœ ν–‰μˆ˜κ°€ μ—†μœΌλ©΄ TRUE
  • SQL%ISOPEN : μ•”μ‹œμ  μ»€μ„œκ°€ μ—΄λ €μžˆλŠ”μ§€ μ—¬λΆ€ 확인, 항상 FALSE (PL/SQL은 μ‹€ν–‰ ν›„ λ°”λ‘œ λ¬΅μ‹œμ  μ»€μ„œλ₯Ό λ‹«κΈ° λ•Œλ¬Έ)
  • SQL%ROWCOUNT : ν•΄λ‹Ή SQL문에 μ˜ν•΄ λ°˜ν™˜λœ 총 ν–‰μˆ˜, κ°€μž₯ 졜근 μˆ˜ν–‰λœ SQL문에 μ˜ν•΄ 영ν–₯ 받은 ν–‰μˆ˜

1-1-2. μ•”μ‹œμ  μ»€μ„œ μ˜ˆμ‹œ

DECLARE
    v_user_num NUMBER := 8;
    v_cnt NUMBER;
BEGIN
    DELETE FROM USER_INFO WHERE USER_NUM > v_user_num;
    dbms_output.put_line('DELETE 건수 : ' || SQL%ROWCOUNT);
    
    IF SQL%FOUND THEN   dbms_output.put_line('패치 둜우 수 1개 이상');
    ELSE                dbms_output.put_line('패치 둜우 수 0개');
    END IF;
    IF SQL%ISOPEN THEN  dbms_output.put_line('μ»€μ„œ μ—΄λ¦Ό');
    ELSE                dbms_output.put_line('μ»€μ„œ λ‹«νž˜');
    END IF;
END;
/
/* κ²°κ³Ό
DELETE 건수 : 1
패치 둜우 수 1개 이상
μ»€μ„œ λ‹«νž˜ */

1-2. λͺ…μ‹œμ  μ»€μ„œ(Explict Cursor)

  • μ‚¬μš©μžκ°€ 직접 μ •μ˜ν•΄μ„œ μ‚¬μš©ν•˜λŠ” μ»€μ„œ
  • κ²°κ³Ό 데이터 집합을 ROW λ³„λ‘œ μ°Έμ‘°ν•΄ μž‘μ—…ν•΄μ•Ό ν•  λ•Œ μœ μš©ν•¨
    • PL/SQLμ—μ„œ SELECTλ¬Έ μ‚¬μš© μ‹œ INTOλ₯Ό ν•¨κ»˜ μ‚¬μš©ν•΄μ•Ό ν•˜λ©°, 항상 λ‹¨μΌν–‰λ§Œ 리턴 λ°›μ•„μ•Ό 함
    • μ»€μ„œ μ‚¬μš© μ‹œ SQL 처리 κ²°κ³Ό 집합을 가져와 λ³΅μˆ˜ν–‰μ— λŒ€ν•œ μž‘μ—…λ„ κ°€λŠ₯

1-2-1. λͺ…μ‹œμ  μ»€μ„œ 속성

  • %FOUND : FETCHν•œ 데이터가 행을 λ°˜ν™˜ν•˜λ©΄ TRUE
  • %NOTFOUND : FETCHν•œ 데이터가 행을 λ°˜ν™˜ν•˜μ§€ μ•ŠμœΌλ©΄ TRUE (LOOP μ’…λ£Œν•  μ‹œμ μ„ 찾음)
  • %ISOPEN : μ»€μ„œκ°€ OPENλ˜μ–΄ 있으면 TRUE
  • %ROWCOUNT : ν˜„μž¬κΉŒμ§€ λ°˜ν™˜λœ λͺ¨λ“  ν–‰μ˜ 수

1-2-2. λͺ…μ‹œμ  μ»€μ„œ 문법

  • μ»€μ„œ μ—΄κΈ° OPEN
    • κ²°κ³Ό ν–‰ 집합을 식별
    • μ»€μ„œ μ•ˆμ˜ 검색이 μ‹€ν–‰λ˜λ©° μ•„λ¬΄λŸ° 데이터 행을 μΆ”μΆœν•˜μ§€ λͺ»ν•΄λ„ μ—λŸ¬κ°€ λ°œμƒν•˜μ§€ μ•ŠμŒ
  • μ»€μ„œ 패치 FETCH
    • ν˜„μž¬ 행을 λ³€μˆ˜μ— λ‘œλ“œ(ν˜„μž¬ 행이 없을 λ•ŒκΉŒμ§€ μˆ˜ν–‰ν•  수 있음)
    • ν˜„μž¬ 데이터 행을 OUTPUT λ³€μˆ˜μ— λ°˜ν™˜
    • μ»€μ„œμ˜ SELECT문의 컬럼과 OUTPUT λ³€μˆ˜μ˜ νƒ€μž…, κ°œμˆ˜κ°€ 동일해야 함
    • μ»€μ„œλŠ” ν•œ 라인씩 데이터λ₯Ό FETCH함
    • 문법 : FETCH cursor_name INTO variable1, variable2;
  • μ»€μ„œ λ‹«κΈ° CLOSE
    • κ²°κ³Ό ν–‰ 집합을 ν•΄μ œ
    • μ‚¬μš©μ„ 마친 μ»€μ„œλŠ” λ°˜λ“œμ‹œ λ‹«μ•„μ£Όμ–΄μ•Ό 함
    • ν•„μš” μ‹œ μ»€μ„œλ₯Ό λ‹€μ‹œ μ—΄ 수 있음
    • μ»€μ„œλ₯Ό 닫은 μƒνƒœμ—μ„œ FETCH λΆˆκ°€λŠ₯
    • 문법 : CLOSE cursor_name;
  • DECLARE
    • λͺ…λͺ…λœ SQL μ˜μ—­μ„ 생성
DECLARE
    CURSOR [μ»€μ„œλͺ…] IS [SELECT ꡬ문];
BEGIN
    OPEN [μ»€μ„œλͺ…];
    FETCH [μ»€μ„œλͺ…] INTO [λ‘œμ»¬λ³€μˆ˜];
    CLOSE [μ»€μ„œλͺ…];
END;
/

1-2-3. λͺ…μ‹œμ  μ»€μ„œ μ˜ˆμ‹œ

DECLARE 
    v_num NUMBER := 5;
    CURSOR userCursor -- μ»€μ„œ μ •μ˜
    IS
    SELECT * FROM USER_INFO
    WHERE USER_NUM < v_num; 
    userData USER_INFO%ROWTYPE; -- λ³€μˆ˜ μ •μ˜
BEGIN
    OPEN userCursor;
    LOOP
        FETCH userCursor INTO userData; -- ν•˜λ‚˜μ”© λ³€μˆ˜μ— λ„£κΈ°
        EXIT WHEN userCursor%NOTFOUND; -- 더이상 μ—†μœΌλ©΄ EXIT
        dbms_output.put_line(userData.USER_NUM || ' ' || userData.USER_NM);
    END LOOP;
    CLOSE userCursor;
END;
/
  • μœ„μ™€ 같이 μ»€μ„œλ₯Ό μ„ μ–Έν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 방법, μ•„λž˜μ™€ 같이 CURSOR FOR LOOPλ₯Ό μ‚¬μš©ν•˜λŠ” 방법이 있음
  • μ„œλΈŒ 쿼리λ₯Ό ν™œμš©ν•˜μ—¬ CURSOR FOR LOOP μ‚¬μš© > CURSORλ₯Ό μ„ μ–Έν•˜μ§€ μ•Šμ•„λ„ 됨
    • FOR LOOPκ°€ μžλ™μœΌλ‘œ μ»€μ„œλ₯Ό OPEN, CLOSE ν•΄μ€Œ
    • 행이 없을 λ•ŒκΉŒμ§€ FETCH λ˜ν•œ μžλ™μœΌλ‘œ κ°€λŠ₯
    • ROWTYPE에 ν•΄λ‹Ήν•˜λŠ” λ³€μˆ˜λ₯Ό λ”°λ‘œ DECLAREν•  ν•„μš”κ°€ μ—†μŒ (μ•”μ‹œμ μœΌλ‘œ μ„ μ–Έλ˜κΈ° λ•Œλ¬Έ)
-- for문에 λ ˆμ½”λ“œ μ„ μ–Έ(name_rec)
DECLARE 
    CURSOR name_list IS
    SELECT USER_NM FROM USER_INFO;
BEGIN 
    FOR name_rec IN name_list
    LOOP
        dbms_output.put_line(name_rec.USER_NM);
    END LOOP;
END;
/

-- λͺ…μ‹œμ  μ»€μ„œ FOR LOOP
DECLARE
BEGIN
    FOR name_list IN 
        ( SELECT USER_NM FROM USER_INFO )
    LOOP
        dbms_output.put_line(name_list.USER_NM);
    END LOOP;
END;
/
  • CURSOR FOR LOOP은 λ‚΄λΆ€μ μœΌλ‘œ μ²˜λ¦¬λ˜λŠ” λ°μ΄ν„°μ˜ μ–‘, I/O μΈ‘λ©΄μ—μ„œ 보닀 νš¨μœ¨μ μ΄λ―€λ‘œ ꢌμž₯됨

1-3. μ»€μ„œ λ³€μˆ˜

  • λ³€μˆ˜μ˜ νŠΉμ§•μ΄ μžˆλŠ” μ»€μ„œ
    • ν•œ 개 μ΄μƒμ˜ 쿼리λ₯Ό μ—°κ²°ν•΄ μ‚¬μš©ν•  수 있음 (μž¬μ‚¬μš©)
    • μ»€μ„œ λ³€μˆ˜λ₯Ό ν•¨μˆ˜λ‚˜ ν”„λ‘œμ‹œμ €μ˜ λ§€κ°œλ³€μˆ˜λ‘œ 전달 κ°€λŠ₯
    • μ»€μ„œ 속성 μ‚¬μš© (v_cursor%FOUND, ...)
  • μ»€μ„œλŠ” 블둝 μ•ˆμ—μ„œλ§Œ μ‚¬μš© κ°€λŠ₯ν•˜λ©°, ν•œ 번 μ„ μ–Έ ν›„ λ³€κ²½ λΆˆκ°€
  • 블둝이 μ‚¬λΌμ§ˆ λ•Œ μ»€μ„œλ„ 사라지며, λ‹€λ₯Έ λΈ”λ‘μ—μ„œ μ‚¬μš© λΆˆκ°€
  • μ»€μ„œ λ³€μˆ˜λ‘œ μ»€μ„œ μ„ μ–Έν•˜λ©΄, λ‹€λ₯Έ λΈ”λ‘μ—μ„œλ„ μ‚¬μš© κ°€λŠ₯ν•˜λ©° μž¬μ‚¬μš©μ΄ κ°€λŠ₯해짐

1-3-1. μ»€μ„œ λ³€μˆ˜ μ„ μ–Έ

  • RETURN [λ°˜ν™˜ νƒ€μž…] : λ°˜ν™˜ν•˜λŠ” κ²°κ³Ό μ§‘ν•© %ROWTYPE
    • μ»€μ„œκ°€ λ°˜ν™˜ν•˜λŠ” νƒ€μž…μ€ ν•œ 개 μ΄μƒμ˜ 컬럼이 μžˆλŠ” λ ˆμ½”λ“œ νƒ€μž…
    • λ°˜ν™˜ νƒ€μž…μ΄ 있으면 κ°•ν•œ μ»€μ„œ νƒ€μž… / μƒλž΅ν•˜λ©΄ μ•½ν•œ μ»€μ„œ νƒ€μž…
  • SYS_REFCURSOR : 였라클 빌트인 μ»€μ„œ νƒ€μž…
    • λ³„λ„λ‘œ μ»€μ„œ νƒ€μž… μ„ μ–Έν•  ν•„μš” μ—†μŒ (μ•½ν•œ μ»€μ„œ νƒ€μž…)
TYPE [μ»€μ„œ νƒ€μž…λͺ…] IS REF CURSOR [RETURN λ°˜ν™˜ νƒ€μž…];
[μ»€μ„œ λ³€μˆ˜λͺ…] [μ»€μ„œ νƒ€μž…λͺ…];
[μ»€μ„œ νƒ€μž…λͺ…] SYS_REFCURSOR;

-- μ˜ˆμ‹œ
DECLARE
    TYPE userCurType IS REF CURSOR RETURN USER_INFO%ROWTYPE;
    testCursor SYS_REFCURSOR;
BEGIN
    (μƒλž΅)
END
;
/

1-3-2. μ»€μ„œ λ³€μˆ˜ μ‚¬μš©

  • CLOSEλŠ” 직접할 ν•„μš”κ°€ μ—†μŒ
OPEN [μ»€μ„œ λ³€μˆ˜λͺ…] FOR SELECT ~ ;
  • 'USER_INFO' ν…Œμ΄λΈ”μ˜ λ ˆμ½”λ“œ νƒ€μž…μœΌλ‘œ μ»€μ„œλ₯Ό λ°›μ•„ μ»¬λŸΌμ„ λͺ¨λ‘ λ ˆμ½”λ“œμ— λ‹΄κ³ , λ ˆμ½”λ“œ νƒ€μž… λ³€μˆ˜λ‘œ μ•ˆμ˜ 속성에 μ ‘κ·Όν•˜μ—¬ 값을 좜λ ₯
DECLARE 
    TYPE userCurType IS REF CURSOR RETURN USER_INFO%ROWTYPE;
    userCur userCurType;
    userData USER_INFO%ROWTYPE;
BEGIN
    -- μ»€μ„œ μ—΄κΈ°
    OPEN userCur FOR SELECT * FROM USER_INFO WHERE USER_NUM < 5;
    -- LOOP λ¬Έ
    LOOP
        FETCH userCur INTO userData;
        dbms_output.put_line(userData.USER_NM || userData.USER_BIRTH);
        EXIT WHEN userCur%NOTFOUND;
    END LOOP;
END;
/
  • λ‹€μŒκ³Ό 같이 μ•½κ²°ν•©μœΌλ‘œ κ°„λž΅ν•˜κ²Œ μ‚¬μš© κ°€λŠ₯
-- λ°˜ν™˜ νƒ€μž… μƒλž΅
DECLARE 
    TYPE userCurType IS REF CURSOR;
    userCur userCurType;
    userData USER_INFO%ROWTYPE;
BEGIN
-- SYS_REFCURSOR
DECLARE 
    userCur SYS_REFCURSOR;
    userData USER_INFO%ROWTYPE;
BEGIN

1-4. μ»€μ„œ ν‘œν˜„μ‹

  • SELECTλ¬Έμ—μ„œ 컬럼 ν˜•νƒœλ‘œ μ»€μ„œλ₯Ό μ‚¬μš©ν•˜λŠ” 것
  • CURSOR(μ„œλΈŒ 쿼리) ν˜•νƒœλ‘œ μ‚¬μš©
  • μ•„λž˜μ™€ 같이 닀쀑행을 λ¦¬ν„΄ν•˜λŠ” μ„œλΈŒ 쿼리둜 인해 였λ₯˜ λ°œμƒ μ‹œ, CURSORλ₯Ό μ΄μš©ν•΄ λͺ¨λ“  행에 λŒ€ν•œ κ²°κ³Ό 확인 κ°€λŠ₯
SELECT 
    USER_NUM
    , USER_NM
    , (
        SELECT STORE_NM FROM STORE_INFO WHERE OWNER_NUM = USER_NUM
    ) AS STORE_NM
FROM USER_INFO;
/* ORA-01427: 단일 ν–‰ ν•˜μœ„ μ§ˆμ˜μ— 2개 μ΄μƒμ˜ 행이 λ¦¬ν„΄λ˜μ—ˆμŠ΅λ‹ˆλ‹€. */

SELECT
    ...
    , CURSOR (
        SELECT STORE_NM FROM STORE_INFO WHERE OWNER_NUM = USER_NUM
    ) AS STORE_NM
    ...
FROM USER_INFO;
/* κ²°κ³Ό
μ„œκ°•μ€€	{<STORE_NM=ꡐ보문고 광화문점>,}
솑강	{}
둜운	{<STORE_NM=신세계 μ˜λ“±ν¬μ >,<STORE_NM=63λΉŒλ”© 아쿠아리움>,} */

1-4-4. 닀쀑행 좜λ ₯

  • 닀쀑행 μ„œλΈŒμΏΌλ¦¬λ₯Ό κ°€λ¦¬ν‚€λŠ” μ»€μ„œλ₯Ό λ³€μˆ˜μ— λ‹΄μ•„ 각각의 ν–‰μœΌλ‘œ 좜λ ₯
    • JAVA의 MAP μ»¬λ ‰μ…˜κ³Ό μœ μ‚¬ν•œ ν˜•νƒœ
DECLARE
    -- μ»€μ„œ μ„ μ–Έ
    CURSOR user_cursor IS
    SELECT 
        USER_NM
        , CURSOR (SELECT STORE_NM FROM STORE_INFO WHERE OWNER_NUM = USER_NUM) AS STORE_NM
    FROM USER_INFO;
    -- νšŒμ› 이름 λ³€μˆ˜
    v_user_name USER_INFO.USER_NM%TYPE;
    -- 상점 이름 좜λ ₯을 μœ„ν•œ μ»€μ„œ λ³€μˆ˜
    store_name_cursor SYS_REFCURSOR;
    -- 상점 이름 λ³€μˆ˜
    v_store_name STORE_INFO.STORE_NM%TYPE;
BEGIN
    OPEN user_cursor;
    
    LOOP
        -- μ»€μ„œ μ•ˆμ˜ 집합을 κ°€μ Έμ˜΄
        FETCH user_cursor INTO v_user_name, store_name_cursor;
        EXIT WHEN user_cursor%NOTFOUND;
        dbms_output.put_line('νšŒμ›λͺ… : ' || v_user_name);
        LOOP
            FETCH store_name_cursor INTO v_store_name;
            EXIT WHEN store_name_cursor%NOTFOUND;
            dbms_output.put_line('  상점λͺ… : ' || v_store_name);
        END LOOP;
    END LOOP;
END;
/
/* κ²°κ³Ό
νšŒμ›λͺ… : μ„œκ°•μ€€
  상점λͺ… : ꡐ보문고 광화문점
νšŒμ›λͺ… : 솑강
νšŒμ›λͺ… : 둜운
  상점λͺ… : 신세계 μ˜λ“±ν¬μ 
  상점λͺ… : 63λΉŒλ”© 아쿠아리움
*/