Ver3.0 ๐ฏ Oracle PL/SQL ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ ์์ ์ ๋ณต! ๐

๐ฏ Oracle PL/SQL ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ ์์ ์ ๋ณต! ๐
๋ฐ์ดํฐ๋ฒ ์ด์ค ์๋ํ์ ํต์ฌ ๋ฌด๊ธฐ, ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ๋ฅผ ์น๊ตฌ์ฒ๋ผ ์ฝ๊ฒ ์๋ ค์ค๊ฒ! ๐ช
๐ ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ, ์ ๋ฐฐ์์ผ ํ ๊น?
์๋ ! ์ค๋์ Oracle ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ง์ง ํ์๋ฅผ ๋๋ ์ ์๋ PL/SQL ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ์ ๋ํด ์ด์ผ๊ธฐํด๋ณผ๊ฒ. ๐ํน์ ๋งค๋ฒ ๋๊ฐ์ SQL ์ฟผ๋ฆฌ๋ฅผ ๋ฐ๋ณตํด์ ์์ฑํ๋ค๊ฐ "์, ์ด๊ฑฐ ํ ๋ฒ์ ์ฒ๋ฆฌํ ์ ์๋?" ํ๊ณ ์๊ฐํด๋ณธ ์ ์์ด? ๋๋ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋์ผ๋ก ๋ญ๊ฐ๋ฅผ ์คํํ๊ณ ์ถ์๋ ์ ์? ๋ฐ๋ก ๊ทธ๋ด ๋ ํ์ํ ๊ฒ ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ์ผ! ๐ฏ
์ค์ ๋ก ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ํ์๊ฐ์ , ๊ฑฐ๋ ์ฒ๋ฆฌ, ๋ก๊ทธ ๊ธฐ๋ก ๋ฑ ์๋ง์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์์ ์ด ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ๋ก ์๋ํ๋์ด ์์ด. ์ด๊ฑธ ๋ฐฐ์ฐ๋ฉด ๋๋ ํ๋ก ๊ฐ๋ฐ์์ฒ๋ผ ํจ์จ์ ์ธ ์์คํ ์ ๋ง๋ค ์ ์๊ฒ ๋ผ! ๐ผ
๐ PL/SQL ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ ์ฐจ๊ทผ์ฐจ๊ทผ
PL/SQL์ด ๋ญ์ผ?
PL/SQL์ Procedural Language/SQL์ ์ฝ์์ผ. ์ฝ๊ฒ ๋งํ๋ฉด SQL์ ํ๋ก๊ทธ๋๋ฐ ๊ธฐ๋ฅ์ ๋ํ ๊ฑฐ์ง! ๐ง์ผ๋ฐ SQL์ "์ด ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ ธ์", "์ ๋ฐ์ดํฐ๋ฅผ ์์ ํด" ๊ฐ์ ๋จ์ ๋ช ๋ น๋ง ๊ฐ๋ฅํด. ํ์ง๋ง PL/SQL์ ๋ณ์ ์ ์ธ, ์กฐ๊ฑด๋ฌธ(IF), ๋ฐ๋ณต๋ฌธ(LOOP), ์์ธ ์ฒ๋ฆฌ๊น์ง ๊ฐ๋ฅํ ์์ ํ ํ๋ก๊ทธ๋๋ฐ ์ธ์ด์ผ. ๋ง์น SQL์ด ๋ ๊ณ ๋ธ๋ก์ด๋ผ๋ฉด, PL/SQL์ ๊ทธ ๋ธ๋ก๋ค์ ์กฐ๋ฆฝํด์ ๋ฉ์ง ์ํ์ ๋ง๋๋ ์ค๊ณ๋ ๊ฐ์ ๊ฑฐ์ง! ๐๏ธ
โ ์ฑ๋ฅ ํฅ์: ์ฌ๋ฌ SQL ๋ฌธ์ ํ ๋ฒ์ ์๋ฒ๋ก ๋ณด๋ด์ ๋คํธ์ํฌ ํธ๋ํฝ ๊ฐ์
โ ๋ชจ๋ํ: ๋ณต์กํ ๋ก์ง์ ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ ๋จ์๋ก ๋ถ๋ฆฌ
โ ๋ณด์ ๊ฐํ: ์ง์ ํ ์ด๋ธ ์ ๊ทผ ๋์ ํ๋ก์์ ๋ฅผ ํตํ ๊ฐ์ ์ ๊ทผ
โ ์ ์ง๋ณด์ ์ฉ์ด: ๋น์ฆ๋์ค ๋ก์ง์ด DB์ ์ง์ค๋์ด ๊ด๋ฆฌ ํธ๋ฆฌ
โ ํธ๋์ญ์ ์ ์ด: COMMIT, ROLLBACK ๋ฑ์ ์ฝ๋ ๋ด์์ ์์ ๋กญ๊ฒ ์ ์ด
PL/SQL ๋ธ๋ก ๊ตฌ์กฐ ์ดํดํ๊ธฐ
PL/SQL์ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ธ๋ก ๋จ์๋ก ์๋ํด. ๋ง์น ๋ ์ํผ์ฒ๋ผ ์์๋๋ก ์คํ๋๋ ๊ตฌ์กฐ์ผ! ๐DECLARE
-- ์ ์ธ๋ถ: ๋ณ์, ์์, ์ปค์ ๋ฑ์ ์ ์ธ
v_employee_name VARCHAR2(100);
v_salary NUMBER;
BEGIN
-- ์คํ๋ถ: ์ค์ ๋ก์ง์ด ๋ค์ด๊ฐ๋ ๋ถ๋ถ
SELECT employee_name, salary
INTO v_employee_name, v_salary
FROM employees
WHERE employee_id = 100;
DBMS_OUTPUT.PUT_LINE('์ง์๋ช
: ' || v_employee_name);
EXCEPTION
-- ์์ธ์ฒ๋ฆฌ๋ถ: ์๋ฌ ๋ฐ์ ์ ์ฒ๋ฆฌ
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('ํด๋น ์ง์์ด ์์ต๋๋ค.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('์ค๋ฅ ๋ฐ์: ' || SQLERRM);
END;
/
์ด ๊ตฌ์กฐ๋ ๋ง์น ์๋ฆฌํ ๋ "์ฌ๋ฃ ์ค๋น(DECLARE) โ ์กฐ๋ฆฌ(BEGIN) โ ๋ฌธ์ ๋ฐ์ ์ ๋์ฒ(EXCEPTION)" ์์์ ๋๊ฐ์! ๐ณ
๐ง ํ๋ก์์ (Procedure) ์์ ์ ๋ณต
ํ๋ก์์ ๊ฐ ๋ญ๊ธธ๋?
ํ๋ก์์ ๋ ํน์ ์์ ์ ์ํํ๋ PL/SQL ๋ธ๋ก์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ ์ฅํด๋ ๊ฑฐ์ผ. ๋ง์น ์์ฃผ ์ฐ๋ ์๋ฆฌ ๋ ์ํผ๋ฅผ ๋ ธํธ์ ์ ์ด๋๋ ๊ฒ์ฒ๋ผ! ๐ํ ๋ฒ ๋ง๋ค์ด๋๋ฉด ์ธ์ ๋ ์ง ์ด๋ฆ๋ง ๋ถ๋ฌ์ ์คํํ ์ ์์ด. ์๋ฅผ ๋ค์ด "ํ์๊ฐ์ ์ฒ๋ฆฌ", "๊ธ์ฌ ๊ณ์ฐ", "์ฌ๊ณ ์ ๋ฐ์ดํธ" ๊ฐ์ ๋ณต์กํ ์์ ๋ค์ ํ๋ก์์ ๋ก ๋ง๋ค์ด๋๋ฉด ์์ฒญ ํธํด์ ธ! ๐
๋ง์ ์ฌ๋๋ค์ด ํท๊ฐ๋ คํ๋ ๋ถ๋ถ์ธ๋ฐ, ๊ฐ๋จํ ์ ๋ฆฌํ๋ฉด:
ํ๋ก์์ : ์์ ์ํ์ด ๋ชฉ์ , ๋ฐํ๊ฐ ์๊ฑฐ๋ ์ฌ๋ฌ ๊ฐ ๊ฐ๋ฅ (OUT ํ๋ผ๋ฏธํฐ)
ํจ์: ๊ฐ ๊ณ์ฐ์ด ๋ชฉ์ , ๋ฐ๋์ ํ๋์ ๊ฐ์ RETURN์ผ๋ก ๋ฐํ
ํ๋ก์์ ๋ "์ผ์ ํด!", ํจ์๋ "๊ฐ์ ๊ณ์ฐํด!"๋ผ๊ณ ์๊ฐํ๋ฉด ๋ผ! ๐ก
ํ๋ก์์ ์์ฑ ๊ธฐ๋ณธ ๋ฌธ๋ฒ
์, ์ด์ ์ค์ ๋ก ํ๋ก์์ ๋ฅผ ๋ง๋ค์ด๋ณผ๊น? ๊ธฐ๋ณธ ๋ฌธ๋ฒ์ ์ด๋ ๊ฒ ์๊ฒผ์ด:CREATE OR REPLACE PROCEDURE ํ๋ก์์ ๋ช
(
ํ๋ผ๋ฏธํฐ1 IN ๋ฐ์ดํฐํ์
,
ํ๋ผ๋ฏธํฐ2 OUT ๋ฐ์ดํฐํ์
,
ํ๋ผ๋ฏธํฐ3 IN OUT ๋ฐ์ดํฐํ์
)
IS
-- ๋ณ์ ์ ์ธ
v_local_var VARCHAR2(100);
BEGIN
-- ์คํ ๋ก์ง
์คํ๋ฌธ๋ค;
EXCEPTION
-- ์์ธ ์ฒ๋ฆฌ
WHEN ์์ธ๋ช
THEN
์ฒ๋ฆฌ๋ฌธ;
END ํ๋ก์์ ๋ช
;
/
ํ๋ผ๋ฏธํฐ ๋ชจ๋ ์ค๋ช
:๐น IN: ์ ๋ ฅ ์ ์ฉ (๊ธฐ๋ณธ๊ฐ, ์๋ต ๊ฐ๋ฅ). ํ๋ก์์ ์์์ ์ฝ๊ธฐ๋ง ๊ฐ๋ฅ
๐น OUT: ์ถ๋ ฅ ์ ์ฉ. ํ๋ก์์ ๊ฐ ๊ฐ์ ๋๋ ค์ค ๋ ์ฌ์ฉ
๐น IN OUT: ์ ์ถ๋ ฅ ๊ฒธ์ฉ. ๊ฐ์ ๋ฐ์์ ์์ ํ ํ ๋ค์ ๋๋ ค์ค
์ค์ ์์ 1: ์ง์ ์ ๋ณด ์กฐํ ํ๋ก์์
๊ฐ์ฅ ๊ธฐ๋ณธ์ ์ธ ์์ ๋ถํฐ ์์ํด๋ณด์! ์ง์ ID๋ฅผ ์ ๋ ฅ๋ฐ์์ ์ด๋ฆ๊ณผ ๊ธ์ฌ๋ฅผ ์กฐํํ๋ ํ๋ก์์ ์ผ:CREATE OR REPLACE PROCEDURE get_employee_info
(
p_emp_id IN NUMBER,
p_emp_name OUT VARCHAR2,
p_salary OUT NUMBER
)
IS
BEGIN
SELECT employee_name, salary
INTO p_emp_name, p_salary
FROM employees
WHERE employee_id = p_emp_id;
DBMS_OUTPUT.PUT_LINE('์กฐํ ์๋ฃ: ' || p_emp_name);
EXCEPTION
WHEN NO_DATA_FOUND THEN
p_emp_name := '์์';
p_salary := 0;
DBMS_OUTPUT.PUT_LINE('ํด๋น ์ง์์ด ์กด์ฌํ์ง ์์ต๋๋ค.');
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('์ค๋ฅ ๋ฐ์: ' || SQLERRM);
RAISE;
END get_employee_info;
/
์คํ ๋ฐฉ๋ฒ:
DECLARE
v_name VARCHAR2(100);
v_sal NUMBER;
BEGIN
get_employee_info(100, v_name, v_sal);
DBMS_OUTPUT.PUT_LINE('์ง์๋ช
: ' || v_name || ', ๊ธ์ฌ: ' || v_sal);
END;
/
์ด๋ ๊ฒ ํ๋ฉด ์ง์ ID 100๋ฒ์ ์ ๋ณด๋ฅผ ๊ฐ์ ธ์ฌ ์ ์์ด! ๊ฐ๋จํ์ง? ๐
์ค์ ์์ 2: ๊ธ์ฌ ์ธ์ ํ๋ก์์
์ด๋ฒ์ ์ข ๋ ์ค์ฉ์ ์ธ ์์ ๋ฅผ ๋ณผ๊น? ํน์ ๋ถ์์ ์ง์๋ค ๊ธ์ฌ๋ฅผ ์ผ๊ด ์ธ์ํ๋ ํ๋ก์์ ์ผ:CREATE OR REPLACE PROCEDURE raise_salary_by_dept
(
p_dept_id IN NUMBER,
p_raise_percent IN NUMBER,
p_affected_count OUT NUMBER
)
IS
v_min_salary NUMBER := 3000000; -- ์ต์ ๊ธ์ฌ
v_max_raise NUMBER := 20; -- ์ต๋ ์ธ์๋ฅ 20%
BEGIN
-- ์ธ์๋ฅ ๊ฒ์ฆ
IF p_raise_percent <= 0 OR p_raise_percent > v_max_raise THEN
RAISE_APPLICATION_ERROR(-20001,
'์ธ์๋ฅ ์ 0๋ณด๋ค ํฌ๊ณ ' || v_max_raise || '% ์ดํ์ฌ์ผ ํฉ๋๋ค.');
END IF;
-- ๊ธ์ฌ ์ธ์ ์คํ
UPDATE employees
SET salary = salary * (1 + p_raise_percent / 100),
last_update_date = SYSDATE
WHERE department_id = p_dept_id
AND salary >= v_min_salary;
-- ์ํฅ๋ฐ์ ํ ์ ์ ์ฅ
p_affected_count := SQL%ROWCOUNT;
-- ์ปค๋ฐ
COMMIT;
DBMS_OUTPUT.PUT_LINE(p_affected_count || '๋ช
์ ๊ธ์ฌ๊ฐ ์ธ์๋์์ต๋๋ค.');
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('๊ธ์ฌ ์ธ์ ์คํจ: ' || SQLERRM);
RAISE;
END raise_salary_by_dept;
/
์ด ํ๋ก์์ ์ ํต์ฌ ํฌ์ธํธ๋ค์ ์ดํด๋ณด์:
1๏ธโฃ ์ ๋ ฅ๊ฐ ๊ฒ์ฆ: ์ธ์๋ฅ ์ด ์ ํจํ ๋ฒ์์ธ์ง ๋จผ์ ์ฒดํฌ
2๏ธโฃ SQL%ROWCOUNT: ๋ง์ง๋ง SQL๋ฌธ์ด ์ํฅ์ ์ค ํ์ ๊ฐ์๋ฅผ ์๋ ค์ค
3๏ธโฃ ํธ๋์ญ์ ์ ์ด: ์ฑ๊ณตํ๋ฉด COMMIT, ์คํจํ๋ฉด ROLLBACK
4๏ธโฃ ์ฌ์ฉ์ ์ ์ ์์ธ: RAISE_APPLICATION_ERROR๋ก ๋ช ํํ ์๋ฌ ๋ฉ์์ง ์ ๋ฌ
5๏ธโฃ ๋น์ฆ๋์ค ๋ก์ง: ์ต์ ๊ธ์ฌ ์ด์์ธ ์ง์๋ง ์ธ์ (์กฐ๊ฑด๋ถ ์ฒ๋ฆฌ)
์ค์ ์์ 3: ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง - ์ฃผ๋ฌธ ์ฒ๋ฆฌ ํ๋ก์์
์ด์ ์ง์ง ์ค๋ฌด์์ ์ธ ๋ฒํ ๋ณต์กํ ํ๋ก์์ ๋ฅผ ๋ง๋ค์ด๋ณผ๊ฒ! ์ฃผ๋ฌธ์ ์ฒ๋ฆฌํ๋ฉด์ ์ฌ๊ณ ํ์ธ, ์ฌ๊ณ ์ฐจ๊ฐ, ์ฃผ๋ฌธ ๊ธฐ๋ก, ํฌ์ธํธ ์ ๋ฆฝ๊น์ง ํ ๋ฒ์ ์ฒ๋ฆฌํ๋ ํ๋ก์์ ์ผ:CREATE OR REPLACE PROCEDURE process_order
(
p_customer_id IN NUMBER,
p_product_id IN NUMBER,
p_quantity IN NUMBER,
p_order_id OUT NUMBER,
p_result_msg OUT VARCHAR2
)
IS
v_stock_quantity NUMBER;
v_product_price NUMBER;
v_total_amount NUMBER;
v_point_earned NUMBER;
-- ์ฌ์ฉ์ ์ ์ ์์ธ
e_insufficient_stock EXCEPTION;
e_invalid_quantity EXCEPTION;
BEGIN
-- 1. ์
๋ ฅ๊ฐ ๊ฒ์ฆ
IF p_quantity <= 0 THEN
RAISE e_invalid_quantity;
END IF;
-- 2. ์ฌ๊ณ ํ์ธ
SELECT stock_quantity, price
INTO v_stock_quantity, v_product_price
FROM products
WHERE product_id = p_product_id
FOR UPDATE; -- ๋์์ฑ ์ ์ด๋ฅผ ์ํ ํ ์ ๊ธ
IF v_stock_quantity < p_quantity THEN
RAISE e_insufficient_stock;
END IF;
-- 3. ์ด ๊ธ์ก ๊ณ์ฐ
v_total_amount := v_product_price * p_quantity;
v_point_earned := TRUNC(v_total_amount * 0.01); -- 1% ํฌ์ธํธ ์ ๋ฆฝ
-- 4. ์ฃผ๋ฌธ ์์ฑ
INSERT INTO orders (order_id, customer_id, product_id, quantity,
total_amount, order_date, status)
VALUES (orders_seq.NEXTVAL, p_customer_id, p_product_id, p_quantity,
v_total_amount, SYSDATE, 'COMPLETED')
RETURNING order_id INTO p_order_id;
-- 5. ์ฌ๊ณ ์ฐจ๊ฐ
UPDATE products
SET stock_quantity = stock_quantity - p_quantity,
last_update_date = SYSDATE
WHERE product_id = p_product_id;
-- 6. ํฌ์ธํธ ์ ๋ฆฝ
UPDATE customers
SET point_balance = point_balance + v_point_earned,
total_purchase_amount = total_purchase_amount + v_total_amount
WHERE customer_id = p_customer_id;
-- 7. ๋ก๊ทธ ๊ธฐ๋ก
INSERT INTO order_logs (log_id, order_id, action, action_date)
VALUES (order_logs_seq.NEXTVAL, p_order_id, 'ORDER_COMPLETED', SYSDATE);
COMMIT;
p_result_msg := '์ฃผ๋ฌธ ์๋ฃ! ์ฃผ๋ฌธ๋ฒํธ: ' || p_order_id ||
', ์ ๋ฆฝ ํฌ์ธํธ: ' || v_point_earned;
EXCEPTION
WHEN e_invalid_quantity THEN
ROLLBACK;
p_result_msg := '์ฃผ๋ฌธ ์๋์ 1๊ฐ ์ด์์ด์ด์ผ ํฉ๋๋ค.';
p_order_id := -1;
WHEN e_insufficient_stock THEN
ROLLBACK;
p_result_msg := '์ฌ๊ณ ๋ถ์กฑ! ํ์ฌ ์ฌ๊ณ : ' || v_stock_quantity || '๊ฐ';
p_order_id := -1;
WHEN NO_DATA_FOUND THEN
ROLLBACK;
p_result_msg := '์กด์ฌํ์ง ์๋ ์ํ์ด๊ฑฐ๋ ๊ณ ๊ฐ์
๋๋ค.';
p_order_id := -1;
WHEN OTHERS THEN
ROLLBACK;
p_result_msg := '์ฃผ๋ฌธ ์ฒ๋ฆฌ ์คํจ: ' || SQLERRM;
p_order_id := -1;
-- ๊ด๋ฆฌ์์๊ฒ ์๋ฆผ (์ค์ ๋ก๋ ๋ณ๋ ์๋ฆผ ํ๋ก์์ ํธ์ถ)
INSERT INTO error_logs (error_id, error_msg, error_date)
VALUES (error_logs_seq.NEXTVAL, SQLERRM, SYSDATE);
COMMIT; -- ์๋ฌ ๋ก๊ทธ๋ ๋ณ๋๋ก ์ปค๋ฐ
END process_order;
/
์! ์ด ํ๋ก์์ ๋ ์ ๋ง ๋ง์ ์ผ์ ํ๊ณ ์์ด! ํ๋์ฉ ๋ฏ์ด๋ณด์:
๐ธ FOR UPDATE: ์ฌ๊ณ ์กฐํ ์ ๋ค๋ฅธ ํธ๋์ญ์ ์ด ๋์์ ์์ ๋ชปํ๊ฒ ํ ์ ๊ธ
๐ธ RETURNING INTO: INSERT ํ ์์ฑ๋ ID๋ฅผ ๋ฐ๋ก ๋ณ์์ ์ ์ฅ
๐ธ ์ฌ์ฉ์ ์ ์ ์์ธ: ๋น์ฆ๋์ค ๊ท์น ์๋ฐ์ ๋ช ํํ๊ฒ ์ฒ๋ฆฌ
๐ธ ์์์ฑ ๋ณด์ฅ: ๋ชจ๋ ์์ ์ด ์ฑ๊ณตํ๊ฑฐ๋ ๋ชจ๋ ์คํจ (All or Nothing)
๐ธ ์๋ฌ ๋ก๊น : ์์ธ ๋ฐ์ ์์๋ ๋ก๊ทธ๋ ๋ณ๋๋ก ์ ์ฅ
๐ธ ๋ช ํํ ๊ฒฐ๊ณผ ๋ฐํ: OUT ํ๋ผ๋ฏธํฐ๋ก ์ฃผ๋ฌธ๋ฒํธ์ ๊ฒฐ๊ณผ ๋ฉ์์ง ์ ๋ฌ
ํ๋ก์์ ๋๋ฒ๊น ๊ณผ ๋ชจ๋ํฐ๋ง
ํ๋ก์์ ๋ฅผ ๋ง๋ค์์ผ๋ฉด ์ ๋๋ก ์๋ํ๋์ง ํ์ธํด์ผ๊ฒ ์ง? ๋๋ฒ๊น ํ์ ์๋ ค์ค๊ฒ! ๐-- 1. DBMS_OUTPUT์ผ๋ก ๋๋ฒ๊น
์ ๋ณด ์ถ๋ ฅ
SET SERVEROUTPUT ON; -- ๋จผ์ ์ด๊ฑธ ์คํํด์ผ ์ถ๋ ฅ์ด ๋ณด์ฌ!
CREATE OR REPLACE PROCEDURE debug_example
IS
v_step VARCHAR2(100);
BEGIN
v_step := 'Step 1: ๋ฐ์ดํฐ ์กฐํ';
DBMS_OUTPUT.PUT_LINE(v_step || ' ์์');
-- ์ค์ ๋ก์ง
DBMS_OUTPUT.PUT_LINE(v_step || ' ์๋ฃ');
v_step := 'Step 2: ๋ฐ์ดํฐ ์ฒ๋ฆฌ';
DBMS_OUTPUT.PUT_LINE(v_step || ' ์์');
-- ์ค์ ๋ก์ง
DBMS_OUTPUT.PUT_LINE(v_step || ' ์๋ฃ');
END;
/
-- 2. ํ๋ก์์ ์คํ ์๊ฐ ์ธก์
DECLARE
v_start_time TIMESTAMP;
v_end_time TIMESTAMP;
v_result VARCHAR2(1000);
v_order_id NUMBER;
BEGIN
v_start_time := SYSTIMESTAMP;
process_order(1001, 2001, 5, v_order_id, v_result);
v_end_time := SYSTIMESTAMP;
DBMS_OUTPUT.PUT_LINE('์คํ ์๊ฐ: ' ||
TO_CHAR((v_end_time - v_start_time) DAY(0) TO SECOND(3)));
DBMS_OUTPUT.PUT_LINE('๊ฒฐ๊ณผ: ' || v_result);
END;
/
1. ๊ณผ๋ํ COMMIT/ROLLBACK ๊ธ์ง: ํธ์ถํ๋ ์ชฝ์์ ํธ๋์ญ์ ์ ์ ์ดํ๊ฒ ํ๋ ๊ฒ ์ข์ ๋๋ ์์ด
2. ์์ธ ์ฒ๋ฆฌ ํ์: ์์ธ๋ฅผ ์ฒ๋ฆฌํ์ง ์์ผ๋ฉด ํธ์ถํ ์ชฝ๊น์ง ์ํฅ์ ์ค
3. ์ฑ๋ฅ ๊ณ ๋ ค: ๋ถํ์ํ SELECT ๋ฌธ์ด๋ ๋ฐ๋ณต๋ฌธ์ ์ฑ๋ฅ ์ ํ์ ์ฃผ๋ฒ
4. ๋ช ๋ช ๊ท์น: ํ๋ผ๋ฏธํฐ๋ p_, ๋ณ์๋ v_, ์ปค์๋ c_ ๊ฐ์ ์ ๋์ฌ ์ฌ์ฉ ๊ถ์ฅ
5. ์ฃผ์ ์์ฑ: ๋ณต์กํ ๋ก์ง์ ๋ฐ๋์ ์ฃผ์์ผ๋ก ์ค๋ช ํด๋ฌ์ผ ๋์ค์ ๊ณ ์ ์ ํด!
โก ํธ๋ฆฌ๊ฑฐ(Trigger) ์์ ์ ๋ณต
ํธ๋ฆฌ๊ฑฐ๊ฐ ๋ญ์ผ?
ํธ๋ฆฌ๊ฑฐ๋ ํน์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ์๋์ผ๋ก ์คํ๋๋ PL/SQL ๋ธ๋ก์ด์ผ! ๋ง์น "๋ฌธ์ด ์ด๋ฆฌ๋ฉด ์๋์ผ๋ก ๋ถ์ด ์ผ์ง๋" ์ผ์๋ฑ์ฒ๋ผ ๋ง์ด์ผ. ๐กํ๋ก์์ ๋ ์ฐ๋ฆฌ๊ฐ ์ง์ ํธ์ถํด์ผ ํ์ง๋ง, ํธ๋ฆฌ๊ฑฐ๋ INSERT, UPDATE, DELETE ๊ฐ์ DML ์์ ์ด ๋ฐ์ํ๋ฉด ์๋์ผ๋ก ์คํ๋ผ. ์ด๊ฒ ํธ๋ฆฌ๊ฑฐ์ ๊ฐ์ฅ ํฐ ํน์ง์ด์ ์ฅ์ ์ด์ง! ๐ฏ
โ ๊ฐ์ฌ(Audit) ๋ก๊ทธ: ๋๊ฐ ์ธ์ ๋ฐ์ดํฐ๋ฅผ ๋ณ๊ฒฝํ๋์ง ์๋ ๊ธฐ๋ก
โ ๋ฐ์ดํฐ ๊ฒ์ฆ: ๋น์ฆ๋์ค ๊ท์น์ ๋ง์ง ์๋ ๋ฐ์ดํฐ ์ ๋ ฅ ๋ฐฉ์ง
โ ์๋ ๊ณ์ฐ: ํฉ๊ณ, ํ๊ท ๋ฑ์ ์๋์ผ๋ก ์ ๋ฐ์ดํธ
โ ์ฐธ์กฐ ๋ฌด๊ฒฐ์ฑ: ๋ณต์กํ ๋ฐ์ดํฐ ๊ด๊ณ๋ฅผ ์๋์ผ๋ก ์ ์ง
โ ์๋ฆผ ๋ฐ์ก: ํน์ ์กฐ๊ฑด ๋ง์กฑ ์ ์๋์ผ๋ก ์๋ฆผ ์ ์ก
โ ์ด๋ ฅ ๊ด๋ฆฌ: ๋ณ๊ฒฝ ์ ๋ฐ์ดํฐ๋ฅผ ์๋์ผ๋ก ์ด๋ ฅ ํ ์ด๋ธ์ ๋ฐฑ์
ํธ๋ฆฌ๊ฑฐ ์ข ๋ฅ์ ํ์ด๋ฐ
ํธ๋ฆฌ๊ฑฐ๋ ์คํ ์์ ๊ณผ ๋ ๋ฒจ์ ๋ฐ๋ผ ์ฌ๋ฌ ์ข ๋ฅ๋ก ๋๋์ด:| ๊ตฌ๋ถ | ์ข ๋ฅ | ์ค๋ช | ์ฌ์ฉ ์ |
|---|---|---|---|
| ์คํ ์์ | BEFORE | DML ์คํ ์ ์ ํธ๋ฆฌ๊ฑฐ ์คํ | ๋ฐ์ดํฐ ๊ฒ์ฆ, ๊ฐ ๋ณ๊ฒฝ |
| AFTER | DML ์คํ ํ์ ํธ๋ฆฌ๊ฑฐ ์คํ | ๋ก๊ทธ ๊ธฐ๋ก, ํต๊ณ ์ ๋ฐ์ดํธ | |
| ์คํ ๋ ๋ฒจ | ROW | ์ํฅ๋ฐ๋ ๊ฐ ํ๋ง๋ค ์คํ | ํ๋ณ ๊ฒ์ฆ, ์ด๋ ฅ ์ ์ฅ |
| STATEMENT | DML ๋ฌธ๋น ํ ๋ฒ๋ง ์คํ | ์ ์ฒด ์์ ๋ก๊ทธ | |
| ์ด๋ฒคํธ | INSERT | ๋ฐ์ดํฐ ์ฝ์ ์ | ์ ๊ท ํ์ ํ์ ๋ฉ์์ง |
| UPDATE | ๋ฐ์ดํฐ ์์ ์ | ๋ณ๊ฒฝ ์ด๋ ฅ ์ ์ฅ | |
| DELETE | ๋ฐ์ดํฐ ์ญ์ ์ | ์ญ์ ์ ๋ฐฑ์ |
ํธ๋ฆฌ๊ฑฐ ๊ธฐ๋ณธ ๋ฌธ๋ฒ
ํธ๋ฆฌ๊ฑฐ์ ๊ธฐ๋ณธ ๋ฌธ๋ฒ์ ์ดํด๋ณด์:CREATE OR REPLACE TRIGGER ํธ๋ฆฌ๊ฑฐ๋ช
{BEFORE | AFTER} {INSERT | UPDATE | DELETE} ON ํ
์ด๋ธ๋ช
[FOR EACH ROW]
[WHEN (์กฐ๊ฑด)]
DECLARE
-- ๋ณ์ ์ ์ธ
BEGIN
-- ์คํ ๋ก์ง
-- :NEW.์ปฌ๋ผ๋ช
- ์๋ก์ด ๊ฐ (INSERT, UPDATE)
-- :OLD.์ปฌ๋ผ๋ช
- ์ด์ ๊ฐ (UPDATE, DELETE)
EXCEPTION
-- ์์ธ ์ฒ๋ฆฌ
END;
/
ํต์ฌ ํค์๋ ์ค๋ช
:๐น :NEW: INSERT๋ UPDATE ํ์ ์๋ก์ด ๊ฐ
๐น :OLD: UPDATE๋ DELETE ์ ์ ์ด์ ๊ฐ
๐น FOR EACH ROW: ํ ๋ ๋ฒจ ํธ๋ฆฌ๊ฑฐ (์๋ตํ๋ฉด ๋ฌธ์ฅ ๋ ๋ฒจ)
๐น WHEN: ํน์ ์กฐ๊ฑด์ ๋ง์กฑํ ๋๋ง ํธ๋ฆฌ๊ฑฐ ์คํ
์ค์ ์์ 1: ๊ฐ์ฌ ๋ก๊ทธ ํธ๋ฆฌ๊ฑฐ
๊ฐ์ฅ ๋ง์ด ์ฐ์ด๋ ๊ฐ์ฌ ๋ก๊ทธ ํธ๋ฆฌ๊ฑฐ๋ฅผ ๋ง๋ค์ด๋ณด์! ์ง์ ์ ๋ณด๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ์๋์ผ๋ก ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๋ ํธ๋ฆฌ๊ฑฐ์ผ:-- ๋จผ์ ๊ฐ์ฌ ๋ก๊ทธ ํ
์ด๋ธ ์์ฑ
CREATE TABLE employee_audit_log (
log_id NUMBER PRIMARY KEY,
employee_id NUMBER,
action_type VARCHAR2(10),
old_name VARCHAR2(100),
new_name VARCHAR2(100),
old_salary NUMBER,
new_salary NUMBER,
changed_by VARCHAR2(100),
changed_date TIMESTAMP
);
CREATE SEQUENCE audit_log_seq START WITH 1;
-- ๊ฐ์ฌ ๋ก๊ทธ ํธ๋ฆฌ๊ฑฐ ์์ฑ
CREATE OR REPLACE TRIGGER trg_employee_audit
AFTER INSERT OR UPDATE OR DELETE ON employees
FOR EACH ROW
DECLARE
v_action VARCHAR2(10);
v_user VARCHAR2(100);
BEGIN
-- ํ์ฌ ์ฌ์ฉ์ ์ ๋ณด ๊ฐ์ ธ์ค๊ธฐ
v_user := USER;
-- ์ด๋ค ์์
์ธ์ง ํ๋จ
IF INSERTING THEN
v_action := 'INSERT';
INSERT INTO employee_audit_log
VALUES (audit_log_seq.NEXTVAL, :NEW.employee_id, v_action,
NULL, :NEW.employee_name, NULL, :NEW.salary,
v_user, SYSTIMESTAMP);
ELSIF UPDATING THEN
v_action := 'UPDATE';
INSERT INTO employee_audit_log
VALUES (audit_log_seq.NEXTVAL, :NEW.employee_id, v_action,
:OLD.employee_name, :NEW.employee_name,
:OLD.salary, :NEW.salary,
v_user, SYSTIMESTAMP);
ELSIF DELETING THEN
v_action := 'DELETE';
INSERT INTO employee_audit_log
VALUES (audit_log_seq.NEXTVAL, :OLD.employee_id, v_action,
:OLD.employee_name, NULL, :OLD.salary, NULL,
v_user, SYSTIMESTAMP);
END IF;
EXCEPTION
WHEN OTHERS THEN
-- ๋ก๊ทธ ๊ธฐ๋ก ์คํจํด๋ ์๋ ์์
์ ๊ณ์ ์งํ
DBMS_OUTPUT.PUT_LINE('๊ฐ์ฌ ๋ก๊ทธ ๊ธฐ๋ก ์คํจ: ' || SQLERRM);
END;
/
์ด ํธ๋ฆฌ๊ฑฐ์ ๋ฉ์ง ์ ๋ค:
1๏ธโฃ ์๋ํ: ๊ฐ๋ฐ์๊ฐ ์ ๊ฒฝ ์ฐ์ง ์์๋ ๋ชจ๋ ๋ณ๊ฒฝ์ฌํญ์ด ์๋ ๊ธฐ๋ก
2๏ธโฃ ์์ ์ฑ: ์ด๋ค ๊ฒฝ๋ก๋ก ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋๋ 100% ์บก์ฒ
3๏ธโฃ ์ถ์ ์ฑ: ๋๊ฐ, ์ธ์ , ๋ฌด์์ ๋ณ๊ฒฝํ๋์ง ์๋ฒฝํ๊ฒ ์ถ์
4๏ธโฃ ๋ณด์: ๋ถ์ ์ ๊ทผ์ด๋ ์ค์๋ก ์ธํ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ๊ฐ์ง
5๏ธโฃ ๋ณต๊ตฌ: ๋ฌธ์ ๋ฐ์ ์ ์ด์ ๊ฐ์ผ๋ก ๋ณต๊ตฌ ๊ฐ๋ฅ
์ค์ ์์ 2: ๋ฐ์ดํฐ ๊ฒ์ฆ ํธ๋ฆฌ๊ฑฐ
์ด๋ฒ์ ์๋ชป๋ ๋ฐ์ดํฐ๊ฐ ๋ค์ด๊ฐ๋ ๊ฑธ ๋ง๋ ๊ฒ์ฆ ํธ๋ฆฌ๊ฑฐ๋ฅผ ๋ง๋ค์ด๋ณด์:CREATE OR REPLACE TRIGGER trg_validate_employee
BEFORE INSERT OR UPDATE ON employees
FOR EACH ROW
DECLARE
v_age NUMBER;
v_dept_exists NUMBER;
BEGIN
-- 1. ๊ธ์ฌ ๊ฒ์ฆ
IF :NEW.salary < 0 THEN
RAISE_APPLICATION_ERROR(-20001, '๊ธ์ฌ๋ 0๋ณด๋ค ์์ ์ ์์ต๋๋ค.');
END IF;
IF :NEW.salary > 100000000 THEN
RAISE_APPLICATION_ERROR(-20002, '๊ธ์ฌ๊ฐ ๋๋ฌด ๋์ต๋๋ค. ํ์ธ์ด ํ์ํฉ๋๋ค.');
END IF;
-- 2. ๋์ด ๊ฒ์ฆ
v_age := TRUNC(MONTHS_BETWEEN(SYSDATE, :NEW.birth_date) / 12);
IF v_age < 18 THEN
RAISE_APPLICATION_ERROR(-20003, '๋ง 18์ธ ๋ฏธ๋ง์ ์ฑ์ฉํ ์ ์์ต๋๋ค.');
END IF;
IF v_age > 70 THEN
RAISE_APPLICATION_ERROR(-20004, '์ ๋
์ ์ด๊ณผํ์ต๋๋ค.');
END IF;
-- 3. ๋ถ์ ์กด์ฌ ์ฌ๋ถ ํ์ธ
IF :NEW.department_id IS NOT NULL THEN
SELECT COUNT(*)
INTO v_dept_exists
FROM departments
WHERE department_id = :NEW.department_id;
IF v_dept_exists = 0 THEN
RAISE_APPLICATION_ERROR(-20005, '์กด์ฌํ์ง ์๋ ๋ถ์์
๋๋ค.');
END IF;
END IF;
-- 4. ์ด๋ฉ์ผ ํ์ ๊ฒ์ฆ
IF :NEW.email IS NOT NULL THEN
IF NOT REGEXP_LIKE(:NEW.email, '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$') THEN
RAISE_APPLICATION_ERROR(-20006, '์ฌ๋ฐ๋ฅธ ์ด๋ฉ์ผ ํ์์ด ์๋๋๋ค.');
END IF;
END IF;
-- 5. ์ ํ๋ฒํธ ํ์ ์๋ ์ ๋ฆฌ
IF :NEW.phone_number IS NOT NULL THEN
:NEW.phone_number := REGEXP_REPLACE(:NEW.phone_number, '[^0-9]', '');
END IF;
-- 6. ์๋์ผ๋ก ์์ฑ์ผ์ ์ค์ (INSERT ์์๋ง)
IF INSERTING THEN
:NEW.created_date := SYSDATE;
:NEW.created_by := USER;
END IF;
-- 7. ์๋์ผ๋ก ์์ ์ผ์ ์ค์ (UPDATE ์์๋ง)
IF UPDATING THEN
:NEW.last_update_date := SYSDATE;
:NEW.last_update_by := USER;
END IF;
END;
/
์! ์ด ํธ๋ฆฌ๊ฑฐ๋ ์ ๋ง ๋ง์ ๊ฒ์ฆ์ ํ๊ณ ์์ด! ์ด๋ ๊ฒ ํ๋ฉด ์ ํ๋ฆฌ์ผ์ด์
์ฝ๋์์ ์ผ์ผ์ด ๊ฒ์ฆํ์ง ์์๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋ ๋ฒจ์์ ๋ฐ์ดํฐ ํ์ง์ ๋ณด์ฅํ ์ ์์ด. ๐ก๏ธ
์ค์ ์์ 3: ์๋ ๊ณ์ฐ ๋ฐ ํต๊ณ ์ ๋ฐ์ดํธ ํธ๋ฆฌ๊ฑฐ
์ฃผ๋ฌธ์ด ๋ค์ด์ฌ ๋๋ง๋ค ๊ณ ๊ฐ์ ์ด ๊ตฌ๋งค๊ธ์ก๊ณผ ๊ตฌ๋งคํ์๋ฅผ ์๋์ผ๋ก ์ ๋ฐ์ดํธํ๋ ํธ๋ฆฌ๊ฑฐ์ผ:CREATE OR REPLACE TRIGGER trg_update_customer_stats
AFTER INSERT OR UPDATE OR DELETE ON orders
FOR EACH ROW
DECLARE
v_customer_id NUMBER;
v_amount_change NUMBER := 0;
v_count_change NUMBER := 0;
BEGIN
-- ์ด๋ค ์์
์ธ์ง์ ๋ฐ๋ผ ๋ณ๊ฒฝ๊ฐ ๊ณ์ฐ
IF INSERTING THEN
v_customer_id := :NEW.customer_id;
v_amount_change := :NEW.total_amount;
v_count_change := 1;
ELSIF UPDATING THEN
v_customer_id := :NEW.customer_id;
v_amount_change := :NEW.total_amount - :OLD.total_amount;
-- ์ฃผ๋ฌธ ์๋ ๋ณ๊ฒฝ ์์
ELSIF DELETING THEN
v_customer_id := :OLD.customer_id;
v_amount_change := -:OLD.total_amount;
v_count_change := -1;
END IF;
-- ๊ณ ๊ฐ ํต๊ณ ์
๋ฐ์ดํธ
UPDATE customers
SET total_purchase_amount = total_purchase_amount + v_amount_change,
total_order_count = total_order_count + v_count_change,
last_order_date = CASE
WHEN INSERTING OR UPDATING THEN SYSDATE
ELSE last_order_date
END,
customer_grade = CASE
WHEN total_purchase_amount + v_amount_change >= 10000000 THEN 'VIP'
WHEN total_purchase_amount + v_amount_change >= 5000000 THEN 'GOLD'
WHEN total_purchase_amount + v_amount_change >= 1000000 THEN 'SILVER'
ELSE 'BRONZE'
END
WHERE customer_id = v_customer_id;
EXCEPTION
WHEN OTHERS THEN
-- ํต๊ณ ์
๋ฐ์ดํธ ์คํจ ์ ๋ก๊ทธ ๊ธฐ๋ก
INSERT INTO error_logs (error_id, error_msg, error_date)
VALUES (error_logs_seq.NEXTVAL,
'๊ณ ๊ฐ ํต๊ณ ์
๋ฐ์ดํธ ์คํจ: ' || SQLERRM, SYSDATE);
-- ์๋ ์์
์ ๊ณ์ ์งํ (์์ธ๋ฅผ ๋ค์ ๋ฐ์์ํค์ง ์์)
END;
/
์ด ํธ๋ฆฌ๊ฑฐ๋ ์ฃผ๋ฌธ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ๊ณ ๊ฐ์ ๋ฑ๊ธ๊น์ง ์๋์ผ๋ก ์ฌ๊ณ์ฐํด์ค! ์ค๋ฌด์์ ์ ๋ง ์ ์ฉํ๊ฒ ์ฐ์ด๋ ํจํด์ด์ผ. ๐๏ธ
์ค์ ์์ 4: ๋ณตํฉ ์ด๋ฒคํธ ํธ๋ฆฌ๊ฑฐ (COMPOUND TRIGGER)
Oracle 11g๋ถํฐ ์ง์๋๋ ๋ณตํฉ ํธ๋ฆฌ๊ฑฐ๋ ์ฌ๋ฌ ํ์ด๋ฐ ํฌ์ธํธ๋ฅผ ํ๋์ ํธ๋ฆฌ๊ฑฐ์์ ์ฒ๋ฆฌํ ์ ์์ด. ์ฑ๋ฅ๋ ๋ ์ข๊ณ ์ฝ๋๋ ๊น๋ํด์ ธ!CREATE OR REPLACE TRIGGER trg_employee_complex
FOR INSERT OR UPDATE OR DELETE ON employees
COMPOUND TRIGGER
-- ํจํค์ง ๋ ๋ฒจ ๋ณ์ (๋ชจ๋ ํ์ด๋ฐ ํฌ์ธํธ์์ ๊ณต์ )
TYPE t_emp_ids IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
v_affected_emp_ids t_emp_ids;
v_counter PLS_INTEGER := 0;
-- BEFORE STATEMENT: DML ๋ฌธ ์คํ ์ (ํ ๋ฒ๋ง)
BEFORE STATEMENT IS
BEGIN
DBMS_OUTPUT.PUT_LINE('=== ์ง์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์์ ===');
v_counter := 0;
END BEFORE STATEMENT;
-- BEFORE EACH ROW: ๊ฐ ํ ์ฒ๋ฆฌ ์
BEFORE EACH ROW IS
BEGIN
IF INSERTING THEN
-- ์ ๊ท ์ง์๋ฒํธ ์๋ ์์ฑ
IF :NEW.employee_id IS NULL THEN
:NEW.employee_id := employees_seq.NEXTVAL;
END IF;
-- ์
์ฌ์ผ ์๋ ์ค์
IF :NEW.hire_date IS NULL THEN
:NEW.hire_date := SYSDATE;
END IF;
END IF;
IF UPDATING THEN
-- ๊ธ์ฌ ์ธ์๋ฅ ์ ํ (ํ ๋ฒ์ 50% ์ด์ ์ธ์ ๋ถ๊ฐ)
IF :NEW.salary > :OLD.salary * 1.5 THEN
RAISE_APPLICATION_ERROR(-20010,
'๊ธ์ฌ๋ ํ ๋ฒ์ 50% ์ด์ ์ธ์ํ ์ ์์ต๋๋ค.');
END IF;
END IF;
END BEFORE EACH ROW;
-- AFTER EACH ROW: ๊ฐ ํ ์ฒ๋ฆฌ ํ
AFTER EACH ROW IS
BEGIN
v_counter := v_counter + 1;
IF INSERTING OR UPDATING THEN
v_affected_emp_ids(v_counter) := :NEW.employee_id;
ELSIF DELETING THEN
v_affected_emp_ids(v_counter) := :OLD.employee_id;
END IF;
END AFTER EACH ROW;
-- AFTER STATEMENT: DML ๋ฌธ ์คํ ํ (ํ ๋ฒ๋ง)
AFTER STATEMENT IS
v_dept_id NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE('์ด ' || v_counter || '๊ฑด์ ๋ฐ์ดํฐ๊ฐ ๋ณ๊ฒฝ๋์์ต๋๋ค.');
-- ์ํฅ๋ฐ์ ๋ถ์์ ํต๊ณ ์
๋ฐ์ดํธ
FOR i IN 1..v_counter LOOP
SELECT department_id INTO v_dept_id
FROM employees
WHERE employee_id = v_affected_emp_ids(i);
-- ๋ถ์๋ณ ํต๊ณ ์
๋ฐ์ดํธ (์ค๋ณต ์ ๊ฑฐ๋ ๋ณ๋ ๋ก์ง ํ์)
UPDATE departments
SET employee_count = (
SELECT COUNT(*) FROM employees
WHERE department_id = v_dept_id
),
avg_salary = (
SELECT AVG(salary) FROM employees
WHERE department_id = v_dept_id
)
WHERE department_id = v_dept_id;
END LOOP;
DBMS_OUTPUT.PUT_LINE('=== ์ง์ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์๋ฃ ===');
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE('ํต๊ณ ์
๋ฐ์ดํธ ์คํจ: ' || SQLERRM);
END AFTER STATEMENT;
END trg_employee_complex;
/
๋ณตํฉ ํธ๋ฆฌ๊ฑฐ์ ์ฅ์ :
โจ ๋ณ์ ๊ณต์ : ๋ชจ๋ ํ์ด๋ฐ ํฌ์ธํธ์์ ๊ฐ์ ๋ณ์ ์ฌ์ฉ ๊ฐ๋ฅ
โจ ์ฑ๋ฅ ํฅ์: ์ฌ๋ฌ ๊ฐ์ ํธ๋ฆฌ๊ฑฐ๋ฅผ ํ๋๋ก ํตํฉํด์ ์ค๋ฒํค๋ ๊ฐ์
โจ ์ฝ๋ ๊ฐ๋ ์ฑ: ๊ด๋ จ ๋ก์ง์ด ํ ๊ณณ์ ๋ชจ์ฌ์์ด ์ดํดํ๊ธฐ ์ฌ์
โจ ๋ฎคํ ์ดํ ํ ์ด๋ธ ๋ฌธ์ ํด๊ฒฐ: AFTER STATEMENT์์ ์ง๊ณ ์ฒ๋ฆฌ ๊ฐ๋ฅ
โจ ์ผ๊ด ์ฒ๋ฆฌ: ์ฌ๋ฌ ํ์ ๋ณ๊ฒฝ์ฌํญ์ ๋ชจ์์ ํ ๋ฒ์ ์ฒ๋ฆฌ
ํธ๋ฆฌ๊ฑฐ ์ฌ์ฉ ์ ์ฃผ์์ฌํญ๊ณผ ๋ฒ ์คํธ ํ๋ํฐ์ค
ํธ๋ฆฌ๊ฑฐ๋ ๊ฐ๋ ฅํ์ง๋ง ์๋ชป ์ฌ์ฉํ๋ฉด ๋ ์ด ๋ ์ ์์ด. ์ฃผ์์ฌํญ์ ๊ผญ ์์๋์! โ ๏ธ1. ๋ฎคํ ์ดํ ํ ์ด๋ธ ๋ฌธ์
ํ ๋ ๋ฒจ ํธ๋ฆฌ๊ฑฐ์์ ๊ฐ์ ํ ์ด๋ธ์ SELECT/UPDATEํ๋ฉด ์๋ฌ ๋ฐ์
โ ํด๊ฒฐ: ๋ณตํฉ ํธ๋ฆฌ๊ฑฐ ์ฌ์ฉ ๋๋ ์์ ํ ์ด๋ธ ํ์ฉ
2. ๋ฌดํ ๋ฃจํ ์ํ
ํธ๋ฆฌ๊ฑฐ A๊ฐ ํ ์ด๋ธ B๋ฅผ ์์ โ ํธ๋ฆฌ๊ฑฐ B๊ฐ ํ ์ด๋ธ A๋ฅผ ์์ โ ๋ฌดํ ๋ฐ๋ณต
โ ํด๊ฒฐ: ํธ๋ฆฌ๊ฑฐ ์ฒด์ธ ์ต์ํ, ์กฐ๊ฑด๋ฌธ์ผ๋ก ์ฌ๊ท ๋ฐฉ์ง
3. ์ฑ๋ฅ ์ ํ
๋ชจ๋ DML๋ง๋ค ํธ๋ฆฌ๊ฑฐ ์คํ์ผ๋ก ์ฑ๋ฅ ์ํฅ
โ ํด๊ฒฐ: ๊ผญ ํ์ํ ๊ฒฝ์ฐ๋ง ์ฌ์ฉ, ๋ณต์กํ ๋ก์ง์ ํ๋ก์์ ๋ก ๋ถ๋ฆฌ
4. ๋๋ฒ๊น ์ด๋ ค์
ํธ๋ฆฌ๊ฑฐ๋ ์๋ ์คํ๋์ด ๋ฌธ์ ์์ธ ํ์ ์ด ์ด๋ ค์
โ ํด๊ฒฐ: ์ถฉ๋ถํ ๋ก๊น , ์๋ฌ ๋ฉ์์ง ๋ช ํํ๊ฒ ์์ฑ
5. ํธ๋์ญ์ ์ ์ด ์ ํ
ํธ๋ฆฌ๊ฑฐ ๋ด์์ COMMIT/ROLLBACK ์ฌ์ฉ ๋ถ๊ฐ
โ ํด๊ฒฐ: ์์จ ํธ๋์ญ์ (PRAGMA AUTONOMOUS_TRANSACTION) ์ฌ์ฉ
์์จ ํธ๋์ญ์ ํ์ฉ
ํธ๋ฆฌ๊ฑฐ์์ ๋ ๋ฆฝ์ ์ธ ํธ๋์ญ์ ์ด ํ์ํ ๋ ์์จ ํธ๋์ญ์ ์ ์ฌ์ฉํด:CREATE OR REPLACE TRIGGER trg_error_logging
AFTER INSERT OR UPDATE OR DELETE ON critical_data
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION; -- ์์จ ํธ๋์ญ์
์ ์ธ
BEGIN
-- ์ด ํธ๋ฆฌ๊ฑฐ์ ์์
์ ๋ฉ์ธ ํธ๋์ญ์
๊ณผ ๋
๋ฆฝ์
INSERT INTO audit_log (log_id, action, log_date)
VALUES (audit_seq.NEXTVAL,
CASE
WHEN INSERTING THEN 'INSERT'
WHEN UPDATING THEN 'UPDATE'
WHEN DELETING THEN 'DELETE'
END,
SYSDATE);
COMMIT; -- ์์จ ํธ๋์ญ์
์ด๋ฏ๋ก COMMIT ๊ฐ๋ฅ!
EXCEPTION
WHEN OTHERS THEN
ROLLBACK; -- ๋ก๊ทธ ๊ธฐ๋ก ์คํจํด๋ ๋ฉ์ธ ํธ๋์ญ์
์ ์ํฅ ์์
END;
/
์์จ ํธ๋์ญ์
์ ๋ฉ์ธ ํธ๋์ญ์
์ด ๋กค๋ฐฑ๋์ด๋ ์ํฅ์ ๋ฐ์ง ์์. ๋ก๊ทธ ๊ธฐ๋ก์ด๋ ์๋ฆผ ๋ฐ์ก ๊ฐ์ ์์
์ ์ ์ฉํด! ๐
๐จ ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ ๊ณ ๊ธ ํ์ฉ ํจํด
ํจํด 1: ํ๋ก์์ ์์ ํธ๋ฆฌ๊ฑฐ ์ ์ดํ๊ธฐ
๋๋ก๋ ํ๋ก์์ ์คํ ์ค์ ํธ๋ฆฌ๊ฑฐ๋ฅผ ์ผ์์ ์ผ๋ก ๋นํ์ฑํํด์ผ ํ ๋๊ฐ ์์ด. ๋๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ ์ฑ๋ฅ ํฅ์์ ์ํด์์ง:CREATE OR REPLACE PROCEDURE bulk_update_employees
(
p_dept_id IN NUMBER,
p_raise_percent IN NUMBER
)
IS
BEGIN
-- ํธ๋ฆฌ๊ฑฐ ๋นํ์ฑํ
EXECUTE IMMEDIATE 'ALTER TRIGGER trg_employee_audit DISABLE';
-- ๋๋ ์
๋ฐ์ดํธ ์ํ
UPDATE employees
SET salary = salary * (1 + p_raise_percent / 100)
WHERE department_id = p_dept_id;
COMMIT;
-- ํธ๋ฆฌ๊ฑฐ ๋ค์ ํ์ฑํ
EXECUTE IMMEDIATE 'ALTER TRIGGER trg_employee_audit ENABLE';
DBMS_OUTPUT.PUT_LINE(SQL%ROWCOUNT || '๊ฑด ์
๋ฐ์ดํธ ์๋ฃ');
EXCEPTION
WHEN OTHERS THEN
-- ์๋ฌ ๋ฐ์ ์์๋ ํธ๋ฆฌ๊ฑฐ๋ ๋ฐ๋์ ๋ค์ ํ์ฑํ
EXECUTE IMMEDIATE 'ALTER TRIGGER trg_employee_audit ENABLE';
RAISE;
END;
/
ํจํด 2: ๋์ SQL ํ์ฉ
์ํฉ์ ๋ฐ๋ผ ๋ค๋ฅธ ํ ์ด๋ธ์ด๋ ์ปฌ๋ผ์ ์ฒ๋ฆฌํด์ผ ํ ๋ ๋์ SQL์ ์ฌ์ฉํด:CREATE OR REPLACE PROCEDURE dynamic_table_backup
(
p_table_name IN VARCHAR2,
p_where_clause IN VARCHAR2 DEFAULT NULL
)
IS
v_sql VARCHAR2(4000);
v_backup_table VARCHAR2(100);
v_count NUMBER;
BEGIN
-- ๋ฐฑ์
ํ
์ด๋ธ๋ช
์์ฑ
v_backup_table := p_table_name || '_BACKUP_' ||
TO_CHAR(SYSDATE, 'YYYYMMDD_HH24MISS');
-- 1. ๋ฐฑ์
ํ
์ด๋ธ ์์ฑ
v_sql := 'CREATE TABLE ' || v_backup_table ||
' AS SELECT * FROM ' || p_table_name;
IF p_where_clause IS NOT NULL THEN
v_sql := v_sql || ' WHERE ' || p_where_clause;
ELSE
v_sql := v_sql || ' WHERE 1=1';
END IF;
EXECUTE IMMEDIATE v_sql;
-- 2. ๋ฐฑ์
๋ ํ ์ ํ์ธ
v_sql := 'SELECT COUNT(*) FROM ' || v_backup_table;
EXECUTE IMMEDIATE v_sql INTO v_count;
DBMS_OUTPUT.PUT_LINE('๋ฐฑ์
์๋ฃ: ' || v_backup_table);
DBMS_OUTPUT.PUT_LINE('๋ฐฑ์
๋ ํ ์: ' || v_count);
-- 3. ๋ฐฑ์
๋ก๊ทธ ๊ธฐ๋ก
INSERT INTO backup_log (log_id, original_table, backup_table,
row_count, backup_date)
VALUES (backup_log_seq.NEXTVAL, p_table_name, v_backup_table,
v_count, SYSDATE);
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('๋ฐฑ์
์คํจ: ' || SQLERRM);
RAISE;
END;
/
-- ์ฌ์ฉ ์์
BEGIN
dynamic_table_backup('EMPLOYEES', 'department_id = 10');
END;
/
๋์ SQL์ ์ ์ฐํ์ง๋ง SQL ์ธ์ ์
์ํ์ด ์์ผ๋ ์
๋ ฅ๊ฐ ๊ฒ์ฆ์ ์ฒ ์ ํ ํด์ผ ํด! ๐
ํจํด 3: ์ปค์๋ฅผ ํ์ฉํ ๋ณต์กํ ๋ฐ์ดํฐ ์ฒ๋ฆฌ
์ฌ๋ฌ ํ์ ์ํํ๋ฉด์ ๋ณต์กํ ์ฒ๋ฆฌ๋ฅผ ํด์ผ ํ ๋ ์ปค์๋ฅผ ์ฌ์ฉํด:CREATE OR REPLACE PROCEDURE process_monthly_sales
(
p_year IN NUMBER,
p_month IN NUMBER
)
IS
-- ์ปค์ ์ ์ธ
CURSOR c_sales IS
SELECT customer_id, SUM(total_amount) as monthly_total
FROM orders
WHERE EXTRACT(YEAR FROM order_date) = p_year
AND EXTRACT(MONTH FROM order_date) = p_month
GROUP BY customer_id
HAVING SUM(total_amount) > 0
ORDER BY monthly_total DESC;
v_rank NUMBER := 0;
v_bonus_rate NUMBER;
v_bonus_amount NUMBER;
v_total_processed NUMBER := 0;
BEGIN
DBMS_OUTPUT.PUT_LINE('=== ' || p_year || '๋
' || p_month || '์ ๋งค์ถ ์ฒ๋ฆฌ ์์ ===');
-- ์ปค์ FOR LOOP (์๋์ผ๋ก OPEN, FETCH, CLOSE)
FOR rec IN c_sales LOOP
v_rank := v_rank + 1;
-- ์์์ ๋ฐ๋ฅธ ๋ณด๋์ค์จ ๊ฒฐ์
v_bonus_rate := CASE
WHEN v_rank <= 10 THEN 0.05 -- ์์ 10๋ช
: 5%
WHEN v_rank <= 50 THEN 0.03 -- ์์ 50๋ช
: 3%
WHEN v_rank <= 100 THEN 0.01 -- ์์ 100๋ช
: 1%
ELSE 0
END;
v_bonus_amount := rec.monthly_total * v_bonus_rate;
-- ๋ณด๋์ค ํฌ์ธํธ ์ง๊ธ
IF v_bonus_amount > 0 THEN
UPDATE customers
SET point_balance = point_balance + v_bonus_amount,
last_bonus_date = SYSDATE
WHERE customer_id = rec.customer_id;
-- ๋ณด๋์ค ์ง๊ธ ์ด๋ ฅ ๊ธฐ๋ก
INSERT INTO bonus_history (bonus_id, customer_id, bonus_amount,
bonus_type, bonus_date)
VALUES (bonus_seq.NEXTVAL, rec.customer_id, v_bonus_amount,
'MONTHLY_SALES', SYSDATE);
v_total_processed := v_total_processed + 1;
END IF;
-- 100๋ช
๋ง๋ค ์ค๊ฐ ์ปค๋ฐ (๋๋ ์ฒ๋ฆฌ ์ ์ฑ๋ฅ ํฅ์)
IF MOD(v_rank, 100) = 0 THEN
COMMIT;
DBMS_OUTPUT.PUT_LINE(v_rank || '๋ช
์ฒ๋ฆฌ ์๋ฃ...');
END IF;
END LOOP;
COMMIT;
DBMS_OUTPUT.PUT_LINE('=== ์ฒ๋ฆฌ ์๋ฃ ===');
DBMS_OUTPUT.PUT_LINE('์ด ์ฒ๋ฆฌ ๊ณ ๊ฐ ์: ' || v_total_processed);
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('์ฒ๋ฆฌ ์คํจ: ' || SQLERRM);
RAISE;
END;
/
์ปค์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ ๋๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์์๋ BULK COLLECT๋ฅผ ๊ณ ๋ คํด๋ด! ๐
ํจํด 4: BULK COLLECT๋ก ์ฑ๋ฅ ์ต์ ํ
์์ฒ, ์๋ง ๊ฑด์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ๋๋ BULK COLLECT๊ฐ ํจ์ฌ ๋นจ๋ผ:CREATE OR REPLACE PROCEDURE bulk_process_orders
(
p_status IN VARCHAR2,
p_batch_size IN NUMBER DEFAULT 1000
)
IS
TYPE t_order_ids IS TABLE OF orders.order_id%TYPE;
TYPE t_customer_ids IS TABLE OF orders.customer_id%TYPE;
TYPE t_amounts IS TABLE OF orders.total_amount%TYPE;
v_order_ids t_order_ids;
v_customer_ids t_customer_ids;
v_amounts t_amounts;
CURSOR c_orders IS
SELECT order_id, customer_id, total_amount
FROM orders
WHERE status = p_status;
v_total_processed NUMBER := 0;
BEGIN
OPEN c_orders;
LOOP
-- ํ ๋ฒ์ p_batch_size๋งํผ ๊ฐ์ ธ์ค๊ธฐ
FETCH c_orders BULK COLLECT INTO
v_order_ids, v_customer_ids, v_amounts
LIMIT p_batch_size;
EXIT WHEN v_order_ids.COUNT = 0;
-- FORALL๋ก ์ผ๊ด ์ฒ๋ฆฌ (๊ฐ๋ณ UPDATE๋ณด๋ค ํจ์ฌ ๋น ๋ฆ)
FORALL i IN 1..v_order_ids.COUNT
UPDATE orders
SET status = 'PROCESSED',
processed_date = SYSDATE
WHERE order_id = v_order_ids(i);
-- ๊ณ ๊ฐ ํฌ์ธํธ ์ผ๊ด ์
๋ฐ์ดํธ
FORALL i IN 1..v_customer_ids.COUNT
UPDATE customers
SET point_balance = point_balance + (v_amounts(i) * 0.01)
WHERE customer_id = v_customer_ids(i);
v_total_processed := v_total_processed + v_order_ids.COUNT;
COMMIT; -- ๋ฐฐ์น๋ง๋ค ์ปค๋ฐ
DBMS_OUTPUT.PUT_LINE(v_total_processed || '๊ฑด ์ฒ๋ฆฌ ์๋ฃ...');
END LOOP;
CLOSE c_orders;
DBMS_OUTPUT.PUT_LINE('=== ์ ์ฒด ์ฒ๋ฆฌ ์๋ฃ: ' || v_total_processed || '๊ฑด ===');
EXCEPTION
WHEN OTHERS THEN
IF c_orders%ISOPEN THEN
CLOSE c_orders;
END IF;
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('์ฒ๋ฆฌ ์คํจ: ' || SQLERRM);
RAISE;
END;
/
์ผ๋ฐ ๋ฃจํ: 10,000๊ฑด ์ฒ๋ฆฌ ์ ์ฝ 30์ด
BULK COLLECT: 10,000๊ฑด ์ฒ๋ฆฌ ์ ์ฝ 3์ด
์ฝ 10๋ฐฐ ์ด์ ๋น ๋ฆ! ๋๋ ๋ฐ์ดํฐ ์ฒ๋ฆฌ ์ ํ์ ๊ธฐ๋ฒ์ด์ผ! ๐จ
ํจํด 5: ์๋ฌ ํธ๋ค๋ง๊ณผ ๋ก๊น ์ ๋ต
์ค๋ฌด์์๋ ์๋ฌ๋ฅผ ์ ์ฒ๋ฆฌํ๊ณ ๊ธฐ๋กํ๋ ๊ฒ ์ ๋ง ์ค์ํด:-- ์๋ฌ ๋ก๊ทธ ํ
์ด๋ธ
CREATE TABLE error_logs (
error_id NUMBER PRIMARY KEY,
procedure_name VARCHAR2(100),
error_code NUMBER,
error_message VARCHAR2(4000),
error_backtrace CLOB,
error_date TIMESTAMP,
additional_info VARCHAR2(4000)
);
CREATE SEQUENCE error_logs_seq;
-- ์๋ฌ ๋ก๊น
ํ๋ก์์
CREATE OR REPLACE PROCEDURE log_error
(
p_procedure_name IN VARCHAR2,
p_additional_info IN VARCHAR2 DEFAULT NULL
)
IS
PRAGMA AUTONOMOUS_TRANSACTION; -- ๋
๋ฆฝ ํธ๋์ญ์
BEGIN
INSERT INTO error_logs (
error_id, procedure_name, error_code, error_message,
error_backtrace, error_date, additional_info
) VALUES (
error_logs_seq.NEXTVAL,
p_procedure_name,
SQLCODE,
SQLERRM,
DBMS_UTILITY.FORMAT_ERROR_BACKTRACE,
SYSTIMESTAMP,
p_additional_info
);
COMMIT; -- ์์จ ํธ๋์ญ์
์ด๋ฏ๋ก ๊ฐ๋ฅ
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
-- ๋ก๊น
์คํจ๋ ์กฐ์ฉํ ๋ฌด์ (๋ฌดํ ๋ฃจํ ๋ฐฉ์ง)
NULL;
END;
/
-- ์๋ฌ ํธ๋ค๋ง์ ์ ์ฉํ ํ๋ก์์
CREATE OR REPLACE PROCEDURE safe_update_employee
(
p_emp_id IN NUMBER,
p_new_salary IN NUMBER
)
IS
v_old_salary NUMBER;
v_dept_id NUMBER;
BEGIN
-- 1. ํ์ฌ ์ ๋ณด ์กฐํ
SELECT salary, department_id
INTO v_old_salary, v_dept_id
FROM employees
WHERE employee_id = p_emp_id;
-- 2. ๋น์ฆ๋์ค ๊ท์น ๊ฒ์ฆ
IF p_new_salary < v_old_salary * 0.5 THEN
RAISE_APPLICATION_ERROR(-20100, '๊ธ์ฌ๋ฅผ 50% ์ด์ ์ญ๊ฐํ ์ ์์ต๋๋ค.');
END IF;
IF p_new_salary > v_old_salary * 2 THEN
RAISE_APPLICATION_ERROR(-20101, '๊ธ์ฌ๋ฅผ 2๋ฐฐ ์ด์ ์ธ์ํ ์ ์์ต๋๋ค.');
END IF;
-- 3. ์
๋ฐ์ดํธ ์ํ
UPDATE employees
SET salary = p_new_salary,
last_update_date = SYSDATE
WHERE employee_id = p_emp_id;
COMMIT;
DBMS_OUTPUT.PUT_LINE('๊ธ์ฌ ์
๋ฐ์ดํธ ์ฑ๊ณต: ' || v_old_salary || ' โ ' || p_new_salary);
EXCEPTION
WHEN NO_DATA_FOUND THEN
log_error('safe_update_employee',
'์กด์ฌํ์ง ์๋ ์ง์ ID: ' || p_emp_id);
DBMS_OUTPUT.PUT_LINE('์ค๋ฅ: ํด๋น ์ง์์ด ์กด์ฌํ์ง ์์ต๋๋ค.');
WHEN OTHERS THEN
ROLLBACK;
log_error('safe_update_employee',
'Employee ID: ' || p_emp_id || ', New Salary: ' || p_new_salary);
DBMS_OUTPUT.PUT_LINE('์ค๋ฅ ๋ฐ์: ' || SQLERRM);
RAISE; -- ์์๋ก ์์ธ ์ ํ
END;
/
์ด๋ ๊ฒ ํ๋ฉด ์๋ฌ๊ฐ ๋ฐ์ํด๋ ์์ธ์ ์ ํํ ํ์
ํ ์ ์์ด! ๐
๐ ๏ธ ์ค์ ํ๋ก์ ํธ: ์ผํ๋ชฐ ์์คํ ๊ตฌ์ถ
์ด์ ๋ฐฐ์ด ๋ด์ฉ์ ์ข ํฉํด์ ์ค์ ์ผํ๋ชฐ ์์คํ ์ ๋ง๋ค์ด๋ณด์! ๐1๋จ๊ณ: ํ ์ด๋ธ ๊ตฌ์กฐ ์ค๊ณ
-- ๊ณ ๊ฐ ํ
์ด๋ธ
CREATE TABLE customers (
customer_id NUMBER PRIMARY KEY,
customer_name VARCHAR2(100) NOT NULL,
email VARCHAR2(200) UNIQUE,
phone VARCHAR2(20),
point_balance NUMBER DEFAULT 0,
customer_grade VARCHAR2(20) DEFAULT 'BRONZE',
total_purchase_amount NUMBER DEFAULT 0,
total_order_count NUMBER DEFAULT 0,
created_date DATE DEFAULT SYSDATE,
last_order_date DATE
);
-- ์ํ ํ
์ด๋ธ
CREATE TABLE products (
product_id NUMBER PRIMARY KEY,
product_name VARCHAR2(200) NOT NULL,
price NUMBER NOT NULL,
stock_quantity NUMBER DEFAULT 0,
category VARCHAR2(50),
is_active VARCHAR2(1) DEFAULT 'Y',
created_date DATE DEFAULT SYSDATE
);
-- ์ฃผ๋ฌธ ํ
์ด๋ธ
CREATE TABLE orders (
order_id NUMBER PRIMARY KEY,
customer_id NUMBER REFERENCES customers(customer_id),
product_id NUMBER REFERENCES products(product_id),
quantity NUMBER NOT NULL,
total_amount NUMBER NOT NULL,
used_points NUMBER DEFAULT 0,
final_amount NUMBER,
order_date DATE DEFAULT SYSDATE,
status VARCHAR2(20) DEFAULT 'PENDING'
);
-- ์ฃผ๋ฌธ ์ด๋ ฅ ํ
์ด๋ธ
CREATE TABLE order_history (
history_id NUMBER PRIMARY KEY,
order_id NUMBER REFERENCES orders(order_id),
old_status VARCHAR2(20),
new_status VARCHAR2(20),
changed_date TIMESTAMP DEFAULT SYSTIMESTAMP,
changed_by VARCHAR2(100)
);
-- ์ํ์ค ์์ฑ
CREATE SEQUENCE customers_seq START WITH 1000;
CREATE SEQUENCE products_seq START WITH 2000;
CREATE SEQUENCE orders_seq START WITH 3000;
CREATE SEQUENCE order_history_seq START WITH 4000;
2๋จ๊ณ: ํต์ฌ ํ๋ก์์ ๊ตฌํ
์ฃผ๋ฌธ ์ฒ๋ฆฌ ํ๋ก์์ (ํฌ์ธํธ ์ฌ์ฉ ํฌํจ):CREATE OR REPLACE PROCEDURE create_order
(
p_customer_id IN NUMBER,
p_product_id IN NUMBER,
p_quantity IN NUMBER,
p_use_points IN NUMBER DEFAULT 0,
p_order_id OUT NUMBER,
p_result_code OUT NUMBER,
p_result_msg OUT VARCHAR2
)
IS
v_product_price NUMBER;
v_stock_quantity NUMBER;
v_customer_points NUMBER;
v_total_amount NUMBER;
v_final_amount NUMBER;
v_earned_points NUMBER;
e_insufficient_stock EXCEPTION;
e_insufficient_points EXCEPTION;
e_invalid_quantity EXCEPTION;
BEGIN
-- ์ด๊ธฐํ
p_result_code := 0;
-- 1. ์
๋ ฅ๊ฐ ๊ฒ์ฆ
IF p_quantity <= 0 THEN
RAISE e_invalid_quantity;
END IF;
-- 2. ์ํ ์ ๋ณด ์กฐํ (์ฌ๊ณ ์ ๊ธ)
SELECT price, stock_quantity
INTO v_product_price, v_stock_quantity
FROM products
WHERE product_id = p_product_id
AND is_active = 'Y'
FOR UPDATE;
-- 3. ์ฌ๊ณ ํ์ธ
IF v_stock_quantity < p_quantity THEN
RAISE e_insufficient_stock;
END IF;
-- 4. ๊ณ ๊ฐ ํฌ์ธํธ ์กฐํ
SELECT point_balance
INTO v_customer_points
FROM customers
WHERE customer_id = p_customer_id
FOR UPDATE;
-- 5. ํฌ์ธํธ ์ฌ์ฉ ๊ฐ๋ฅ ์ฌ๋ถ ํ์ธ
IF p_use_points > v_customer_points THEN
RAISE e_insufficient_points;
END IF;
-- 6. ๊ธ์ก ๊ณ์ฐ
v_total_amount := v_product_price * p_quantity;
v_final_amount := v_total_amount - p_use_points;
IF v_final_amount < 0 THEN
v_final_amount := 0;
END IF;
-- 7. ์ ๋ฆฝ ํฌ์ธํธ ๊ณ์ฐ (์ต์ข
๊ฒฐ์ ๊ธ์ก์ 1%)
v_earned_points := TRUNC(v_final_amount * 0.01);
-- 8. ์ฃผ๋ฌธ ์์ฑ
INSERT INTO orders (
order_id, customer_id, product_id, quantity,
total_amount, used_points, final_amount,
order_date, status
) VALUES (
orders_seq.NEXTVAL, p_customer_id, p_product_id, p_quantity,
v_total_amount, p_use_points, v_final_amount,
SYSDATE, 'COMPLETED'
) RETURNING order_id INTO p_order_id;
-- 9. ์ฌ๊ณ ์ฐจ๊ฐ
UPDATE products
SET stock_quantity = stock_quantity - p_quantity
WHERE product_id = p_product_id;
-- 10. ๊ณ ๊ฐ ์ ๋ณด ์
๋ฐ์ดํธ
UPDATE customers
SET point_balance = point_balance - p_use_points + v_earned_points,
total_purchase_amount = total_purchase_amount + v_final_amount,
total_order_count = total_order_count + 1,
last_order_date = SYSDATE,
customer_grade = CASE
WHEN total_purchase_amount + v_final_amount >= 10000000 THEN 'VIP'
WHEN total_purchase_amount + v_final_amount >= 5000000 THEN 'GOLD'
WHEN total_purchase_amount + v_final_amount >= 1000000 THEN 'SILVER'
ELSE 'BRONZE'
END
WHERE customer_id = p_customer_id;
COMMIT;
p_result_msg := '์ฃผ๋ฌธ ์๋ฃ! ์ฃผ๋ฌธ๋ฒํธ: ' || p_order_id ||
', ์ฌ์ฉ ํฌ์ธํธ: ' || p_use_points ||
', ์ ๋ฆฝ ํฌ์ธํธ: ' || v_earned_points;
EXCEPTION
WHEN e_invalid_quantity THEN
ROLLBACK;
p_result_code := -1;
p_result_msg := '์ฃผ๋ฌธ ์๋์ 1๊ฐ ์ด์์ด์ด์ผ ํฉ๋๋ค.';
WHEN e_insufficient_stock THEN
ROLLBACK;
p_result_code := -2;
p_result_msg := '์ฌ๊ณ ๋ถ์กฑ! ํ์ฌ ์ฌ๊ณ : ' || v_stock_quantity || '๊ฐ';
WHEN e_insufficient_points THEN
ROLLBACK;
p_result_code := -3;
p_result_msg := 'ํฌ์ธํธ ๋ถ์กฑ! ๋ณด์ ํฌ์ธํธ: ' || v_customer_points;
WHEN NO_DATA_FOUND THEN
ROLLBACK;
p_result_code := -4;
p_result_msg := '์กด์ฌํ์ง ์๋ ์ํ์ด๊ฑฐ๋ ๊ณ ๊ฐ์
๋๋ค.';
WHEN OTHERS THEN
ROLLBACK;
p_result_code := -99;
p_result_msg := '์ฃผ๋ฌธ ์ฒ๋ฆฌ ์คํจ: ' || SQLERRM;
log_error('create_order',
'Customer: ' || p_customer_id || ', Product: ' || p_product_id);
END;
/
3๋จ๊ณ: ํธ๋ฆฌ๊ฑฐ ๊ตฌํ
์ฃผ๋ฌธ ์ํ ๋ณ๊ฒฝ ์ด๋ ฅ ํธ๋ฆฌ๊ฑฐ:CREATE OR REPLACE TRIGGER trg_order_status_history
AFTER UPDATE OF status ON orders
FOR EACH ROW
WHEN (OLD.status != NEW.status)
BEGIN
INSERT INTO order_history (
history_id, order_id, old_status, new_status,
changed_date, changed_by
) VALUES (
order_history_seq.NEXTVAL,
:NEW.order_id,
:OLD.status,
:NEW.status,
SYSTIMESTAMP,
USER
);
END;
/
์ฌ๊ณ ๋ถ์กฑ ์๋ฆผ ํธ๋ฆฌ๊ฑฐ:
CREATE OR REPLACE TRIGGER trg_low_stock_alert
AFTER UPDATE OF stock_quantity ON products
FOR EACH ROW
WHEN (NEW.stock_quantity < 10 AND OLD.stock_quantity >= 10)
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
-- ์ฌ๊ณ ๋ถ์กฑ ์๋ฆผ ๋ก๊ทธ ๊ธฐ๋ก
INSERT INTO stock_alerts (
alert_id, product_id, product_name,
current_stock, alert_date
) VALUES (
stock_alerts_seq.NEXTVAL,
:NEW.product_id,
:NEW.product_name,
:NEW.stock_quantity,
SYSDATE
);
COMMIT;
-- ์ค์ ๋ก๋ ์ฌ๊ธฐ์ ๊ด๋ฆฌ์์๊ฒ ์ด๋ฉ์ผ์ด๋ SMS ๋ฐ์ก
DBMS_OUTPUT.PUT_LINE('โ ๏ธ ์ฌ๊ณ ๋ถ์กฑ ์๋ฆผ: ' || :NEW.product_name ||
' (์ฌ๊ณ : ' || :NEW.stock_quantity || '๊ฐ)');
END;
/
4๋จ๊ณ: ํ ์คํธ ๋ฐ ์คํ
-- ํ
์คํธ ๋ฐ์ดํฐ ์
๋ ฅ
INSERT INTO customers (customer_id, customer_name, email, phone)
VALUES (customers_seq.NEXTVAL, 'ํ๊ธธ๋', 'hong@example.com', '010-1234-5678');
INSERT INTO products (product_id, product_name, price, stock_quantity, category)
VALUES (products_seq.NEXTVAL, '๋
ธํธ๋ถ', 1500000, 50, '์ ์์ ํ');
INSERT INTO products (product_id, product_name, price, stock_quantity, category)
VALUES (products_seq.NEXTVAL, '๋ง์ฐ์ค', 30000, 8, '์ ์์ ํ');
COMMIT;
-- ์ฃผ๋ฌธ ํ
์คํธ
DECLARE
v_order_id NUMBER;
v_result_code NUMBER;
v_result_msg VARCHAR2(1000);
BEGIN
create_order(
p_customer_id => 1000,
p_product_id => 2000,
p_quantity => 2,
p_use_points => 0,
p_order_id => v_order_id,
p_result_code => v_result_code,
p_result_msg => v_result_msg
);
DBMS_OUTPUT.PUT_LINE('๊ฒฐ๊ณผ ์ฝ๋: ' || v_result_code);
DBMS_OUTPUT.PUT_LINE('๊ฒฐ๊ณผ ๋ฉ์์ง: ' || v_result_msg);
END;
/
-- ๊ณ ๊ฐ ์ ๋ณด ํ์ธ
SELECT customer_name, point_balance, customer_grade,
total_purchase_amount, total_order_count
FROM customers
WHERE customer_id = 1000;
-- ์ฌ๊ณ ํ์ธ
SELECT product_name, stock_quantity
FROM products
WHERE product_id = 2000;
์๋ฒฝํ๊ฒ ์๋ํ๋ ์ผํ๋ชฐ ์์คํ
์ด ์์ฑ๋์ด! ๐์ค์ ๋ก ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์๋ ์ด๋ฐ ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ๋ค์ด ์๋ฐฑ ๊ฐ์ฉ ๋์๊ฐ๋ฉด์ ๊ฑฐ๋๋ฅผ ์ฒ๋ฆฌํ๊ณ ์์ด. ๋๋ ์ด์ ์ด๋ฐ ์์คํ ์ ๋ง๋ค ์ ์๊ฒ ๋ ๊ฑฐ์ผ! ๐ช
๐ ์ฑ๋ฅ ์ต์ ํ์ ๋ชจ๋ํฐ๋ง
ํ๋ก์์ ์ฑ๋ฅ ๋ถ์
ํ๋ก์์ ๊ฐ ๋๋ฆฌ๋ค๋ฉด ์ด๋๊ฐ ๋ฌธ์ ์ธ์ง ์ฐพ์์ผ๊ฒ ์ง? ์ฑ๋ฅ ๋ถ์ ๋ฐฉ๋ฒ์ ์๋ ค์ค๊ฒ:-- 1. ์คํ ๊ณํ ํ์ธ
EXPLAIN PLAN FOR
SELECT * FROM employees WHERE department_id = 10;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- 2. ํ๋ก์์ ์คํ ํต๊ณ ํ์ธ
SELECT name, type, executions,
ROUND(elapsed_time/1000000, 2) as elapsed_sec,
ROUND(cpu_time/1000000, 2) as cpu_sec
FROM v$sqlarea
WHERE sql_text LIKE '%process_order%'
ORDER BY elapsed_time DESC;
-- 3. ํ๋กํ์ผ๋ง (DBMS_PROFILER ์ฌ์ฉ)
BEGIN
DBMS_PROFILER.START_PROFILER('order_process_profile');
-- ํ
์คํธํ ํ๋ก์์ ์คํ
FOR i IN 1..100 LOOP
process_order(1001, 2001, 5, NULL, NULL);
END LOOP;
DBMS_PROFILER.STOP_PROFILER;
END;
/
-- ํ๋กํ์ผ๋ง ๊ฒฐ๊ณผ ์กฐํ
SELECT u.unit_name, d.line#, d.total_time,
s.text
FROM dbms_profiler_units u
JOIN dbms_profiler_data d ON u.runid = d.runid
AND u.unit_number = d.unit_number
JOIN user_source s ON u.unit_name = s.name
AND d.line# = s.line
WHERE u.runid = (SELECT MAX(runid) FROM dbms_profiler_runs)
ORDER BY d.total_time DESC;
์ฑ๋ฅ ์ต์ ํ ํ
1. ์ธ๋ฑ์ค ํ์ฉ
โ WHERE ์ ์ ์์ฃผ ์ฌ์ฉ๋๋ ์ปฌ๋ผ์ ์ธ๋ฑ์ค ์์ฑ
โ ์กฐ์ธ ์ปฌ๋ผ์ ์ธ๋ฑ์ค ํ์
โ ํ์ง๋ง ์ธ๋ฑ์ค๊ฐ ๋๋ฌด ๋ง์ผ๋ฉด INSERT/UPDATE ์ฑ๋ฅ ์ ํ
2. SQL ์ต์ ํ
โ SELECT *๋ณด๋ค๋ ํ์ํ ์ปฌ๋ผ๋ง ๋ช ์
โ ์๋ธ์ฟผ๋ฆฌ๋ณด๋ค๋ JOIN ์ฌ์ฉ (๋๋ถ๋ถ์ ๊ฒฝ์ฐ)
โ EXISTS vs IN: ๋๋ ๋ฐ์ดํฐ๋ EXISTS๊ฐ ์ ๋ฆฌ
โ UNION ALL vs UNION: ์ค๋ณต ์ ๊ฑฐ๊ฐ ํ์์์ผ๋ฉด UNION ALL
3. ์ปค์ ์ต์ ํ
โ ๊ฐ๋ฅํ๋ฉด BULK COLLECT ์ฌ์ฉ
โ LIMIT ์ ๋ก ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ ์ ์ด
โ ์ปค์๋ ๋ฐ๋์ CLOSE (๋ฉ๋ชจ๋ฆฌ ๋์ ๋ฐฉ์ง)
4. ํธ๋์ญ์ ๊ด๋ฆฌ
โ ๋๋ ์ฒ๋ฆฌ ์ ์ ์ ํ ๋ฐฐ์น ํฌ๊ธฐ๋ก COMMIT
โ ๋ถํ์ํ COMMIT/ROLLBACK ์ต์ํ
โ ๊ธด ํธ๋์ญ์ ์ ๋ฝ ๋๊ธฐ ์๊ฐ ์ฆ๊ฐ
5. ํธ๋ฆฌ๊ฑฐ ์ต์ ํ
โ ํธ๋ฆฌ๊ฑฐ ๋ด ๋ณต์กํ ๋ก์ง์ ํ๋ก์์ ๋ก ๋ถ๋ฆฌ
โ WHEN ์ ๋ก ๋ถํ์ํ ์คํ ๋ฐฉ์ง
โ ํธ๋ฆฌ๊ฑฐ ์ฒด์ธ ์ต์ํ (AโBโC ๊ฐ์ ์ฐ์ ์คํ)
โ ์์จ ํธ๋์ญ์ ์ ๊ผญ ํ์ํ ๋๋ง ์ฌ์ฉ
๋ชจ๋ํฐ๋ง ๋์๋ณด๋ ๊ตฌ์ถ
์ค์๊ฐ์ผ๋ก ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ์ ์ํ๋ฅผ ๋ชจ๋ํฐ๋งํ๋ ๋ทฐ๋ฅผ ๋ง๋ค์ด๋ณด์:-- ํ๋ก์์ ์คํ ํต๊ณ ๋ทฐ
CREATE OR REPLACE VIEW v_procedure_stats AS
SELECT
object_name,
object_type,
status,
created,
last_ddl_time
FROM user_objects
WHERE object_type IN ('PROCEDURE', 'FUNCTION', 'TRIGGER')
ORDER BY last_ddl_time DESC;
-- ์๋ฌ ๋ฐ์ ํต๊ณ ๋ทฐ
CREATE OR REPLACE VIEW v_error_summary AS
SELECT
procedure_name,
error_code,
COUNT(*) as error_count,
MAX(error_date) as last_error_date
FROM error_logs
WHERE error_date >= SYSDATE - 7 -- ์ต๊ทผ 7์ผ
GROUP BY procedure_name, error_code
ORDER BY error_count DESC;
-- ์ฑ๋ฅ ๋ฌธ์ ํ๋ก์์ ์กฐํ
SELECT * FROM v_procedure_stats WHERE status = 'INVALID';
SELECT * FROM v_error_summary WHERE error_count > 10;
๐ ์ค๋ฌด ๋ฒ ์คํธ ํ๋ํฐ์ค
์ฝ๋ฉ ์ปจ๋ฒค์
ํ์์ ์ผํ ๋๋ ์ผ๊ด๋ ์ฝ๋ฉ ์คํ์ผ์ด ์ ๋ง ์ค์ํด:1. ๋ช ๋ช ๊ท์น
โข ํ๋ก์์ /ํจ์: ๋์ฌ๋ก ์์ (get_employee, update_salary)
โข ํธ๋ฆฌ๊ฑฐ: trg_ ์ ๋์ฌ (trg_employee_audit)
โข ํ๋ผ๋ฏธํฐ: p_ ์ ๋์ฌ (p_emp_id, p_salary)
โข ๋ณ์: v_ ์ ๋์ฌ (v_total_amount, v_count)
โข ์ปค์: c_ ์ ๋์ฌ (c_employees, c_orders)
โข ์์: c_ ์ ๋์ฌ + ๋๋ฌธ์ (c_MAX_SALARY)
โข ์์ธ: e_ ์ ๋์ฌ (e_invalid_data)
2. ๋ค์ฌ์ฐ๊ธฐ์ ํฌ๋งทํ
โข ๋ค์ฌ์ฐ๊ธฐ: 4์นธ ๊ณต๋ฐฑ (ํญ ๋์ ์คํ์ด์ค)
โข SQL ํค์๋: ๋๋ฌธ์ (SELECT, FROM, WHERE)
โข ํ ์ด๋ธ/์ปฌ๋ผ๋ช : ์๋ฌธ์ ๋๋ ์ค๋ค์ดํฌ ์ผ์ด์ค
โข ํ ์ค์ ํ๋์ ๋ช ๋ น๋ฌธ
3. ์ฃผ์ ์์ฑ
โข ํ๋ก์์ ์๋จ์ ๋ชฉ์ , ํ๋ผ๋ฏธํฐ, ์์ฑ์, ์์ฑ์ผ ๋ช ์
โข ๋ณต์กํ ๋ก์ง์ ๋ฐ๋์ ์ฃผ์ ์ถ๊ฐ
โข ๋น์ฆ๋์ค ๊ท์น์ ์์ธํ ์ค๋ช
4. ์๋ฌ ์ฒ๋ฆฌ
โข ๋ชจ๋ ํ๋ก์์ ์ EXCEPTION ๋ธ๋ก ํ์
โข ์ฌ์ฉ์ ์ ์ ์๋ฌ๋ -20000 ~ -20999 ๋ฒ์ ์ฌ์ฉ
โข ์๋ฌ ๋ฉ์์ง๋ ๋ช ํํ๊ณ ๊ตฌ์ฒด์ ์ผ๋ก
5. ๋ณด์
โข ๋์ SQL ์ฌ์ฉ ์ SQL ์ธ์ ์ ๋ฐฉ์ง
โข ๋ฏผ๊ฐํ ์ ๋ณด๋ ์ํธํ
โข ๊ถํ์ ์ต์ํ์ผ๋ก๋ง ๋ถ์ฌ
์ฃผ์ ํ ํ๋ฆฟ ์์
/*******************************************************************************
* ํ๋ก์์ ๋ช
: process_monthly_salary
* ์ค๋ช
: ์๋ณ ๊ธ์ฌ๋ฅผ ๊ณ์ฐํ๊ณ ์ง๊ธ ์ฒ๋ฆฌ๋ฅผ ์ํํฉ๋๋ค.
*
* ํ๋ผ๋ฏธํฐ:
* p_year_month (IN) : ์ฒ๋ฆฌํ ๋
์ (YYYYMM ํ์)
* p_dept_id (IN) : ๋ถ์ ID (NULL์ด๋ฉด ์ ์ฒด ๋ถ์)
* p_result_count (OUT): ์ฒ๋ฆฌ๋ ์ง์ ์
* p_result_msg (OUT) : ์ฒ๋ฆฌ ๊ฒฐ๊ณผ ๋ฉ์์ง
*
* ์์ฑ์: ํ๊ธธ๋
* ์์ฑ์ผ: 2024-01-15
* ์์ ์ด๋ ฅ:
* 2024-01-20 ๊น์ฒ ์ - ๋ณด๋์ค ๊ณ์ฐ ๋ก์ง ์ถ๊ฐ
* 2024-02-01 ์ด์ํฌ - ์ฑ๋ฅ ์ต์ ํ (BULK COLLECT ์ ์ฉ)
*
* ์ฌ์ฉ์์:
* DECLARE
* v_count NUMBER;
* v_msg VARCHAR2(1000);
* BEGIN
* process_monthly_salary('202401', NULL, v_count, v_msg);
* DBMS_OUTPUT.PUT_LINE(v_msg);
* END;
*******************************************************************************/
CREATE OR REPLACE PROCEDURE process_monthly_salary
(
p_year_month IN VARCHAR2,
p_dept_id IN NUMBER DEFAULT NULL,
p_result_count OUT NUMBER,
p_result_msg OUT VARCHAR2
)
IS
-- ๋ณ์ ์ ์ธ
v_base_date DATE;
v_total_amount NUMBER := 0;
BEGIN
-- ๋น์ฆ๋์ค ๋ก์ง...
END process_monthly_salary;
/
๋ฒ์ ๊ด๋ฆฌ์ ๋ฐฐํฌ
ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ๋ ์์ค ์ฝ๋๋๊น ๋ฒ์ ๊ด๋ฆฌ๊ฐ ํ์ํด:1. ์์ค ์ฝ๋ ๊ด๋ฆฌ
โข Git ๊ฐ์ VCS์ ๋ชจ๋ DDL ์คํฌ๋ฆฝํธ ์ ์ฅ
โข ํ์ผ๋ช ๊ท์น: ๊ฐ์ฒดํ์ _๊ฐ์ฒด๋ช _๋ฒ์ .sql
์) PROC_process_order_v1.0.sql
โข ๋ณ๊ฒฝ ์ด๋ ฅ์ ์ฃผ์์ผ๋ก ์์ธํ ๊ธฐ๋ก
2. ๋ฐฐํฌ ์คํฌ๋ฆฝํธ
โข ๊ฐ๋ฐ โ ํ ์คํธ โ ์ด์ ์์๋ก ๋ฐฐํฌ
โข ๋กค๋ฐฑ ์คํฌ๋ฆฝํธ๋ ํจ๊ป ์ค๋น
โข ๋ฐฐํฌ ์ ๋ฐ๋์ ๋ฐฑ์
3. ํ ์คํธ
โข ๋จ์ ํ ์คํธ: ๊ฐ ํ๋ก์์ ๋ณ ํ ์คํธ ์ผ์ด์ค ์์ฑ
โข ํตํฉ ํ ์คํธ: ์ ์ฒด ํ๋ก์ธ์ค ์๋๋ฆฌ์ค ํ ์คํธ
โข ์ฑ๋ฅ ํ ์คํธ: ๋๋ ๋ฐ์ดํฐ๋ก ๋ถํ ํ ์คํธ
4. ๋ฌธ์ํ
โข API ๋ฌธ์: ๊ฐ ํ๋ก์์ ์ ์ฌ์ฉ๋ฒ ์ ๋ฆฌ
โข ERD: ํ ์ด๋ธ ๊ด๊ณ๋ ์ต์ ์ํ ์ ์ง
โข ํ๋ก์ฐ์ฐจํธ: ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง ์๊ฐํ
๐ฏ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ฌ๊ธฐ๊น์ง ์ค๋๋ผ ์ ๋ง ์๊ณ ํ์ด! ๐์ฐ๋ฆฌ๊ฐ ํจ๊ป ๋ฐฐ์ด ๋ด์ฉ์ ์ ๋ฆฌํด๋ณด์:
โจ ํต์ฌ ์์ฝ
ํ๋ก์์ ๋ ๋ฐ๋ณต์ ์ธ ์์ ์ ๋ชจ๋ํํด์ ์ฌ์ฌ์ฉ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ ๋์ฌ์ฃผ๋ ๊ฐ๋ ฅํ ๋๊ตฌ์ผ. ํ๋ผ๋ฏธํฐ๋ฅผ ํตํด ์ ์ฐํ๊ฒ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๊ณ ๋ฐ์ ์ ์๊ณ , ๋ณต์กํ ๋น์ฆ๋์ค ๋ก์ง์ ๊น๋ํ๊ฒ ๊ตฌํํ ์ ์์ด. ๐ง
ํธ๋ฆฌ๊ฑฐ๋ ๋ฐ์ดํฐ ๋ณ๊ฒฝ ์ ์๋์ผ๋ก ์คํ๋์ด ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ๋ณด์ฅํ๊ณ , ๊ฐ์ฌ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ณ , ๋ณต์กํ ๋น์ฆ๋์ค ๊ท์น์ ํฌ๋ช ํ๊ฒ ์ ์ฉํ ์ ์๊ฒ ํด์ค. ํ์ง๋ง ๊ณผ๋ํ๊ฒ ์ฌ์ฉํ๋ฉด ์ฑ๋ฅ ๋ฌธ์ ๋ ๋๋ฒ๊น ์ด๋ ค์์ด ์๊ธธ ์ ์์ผ๋ ์ ์คํ๊ฒ ์ฌ์ฉํด์ผ ํด. โก
์ค๋ฌด์์๋ ํ๋ก์์ ์ ํธ๋ฆฌ๊ฑฐ๋ฅผ ์ ์ ํ ์กฐํฉํด์ ์ฌ์ฉํ๋ ๊ฒ ์ค์ํด. ํ๋ก์์ ๋ก ์ฃผ์ ๋น์ฆ๋์ค ๋ก์ง์ ๊ตฌํํ๊ณ , ํธ๋ฆฌ๊ฑฐ๋ก ์๋ํ๊ฐ ํ์ํ ๋ถ๋ถ์ ์ฒ๋ฆฌํ๋ ๊ฑฐ์ง. ๊ทธ๋ฆฌ๊ณ ํญ์ ์ฑ๋ฅ, ๋ณด์, ์ ์ง๋ณด์์ฑ์ ๊ณ ๋ คํด์ผ ํด! ๐ก
์ด์ ๊ธฐ๋ณธ๊ธฐ๋ ํํํด์ก์ผ๋, ๋ค์ ๋จ๊ณ๋ก ๋์๊ฐ๋ณด์:
1๏ธโฃ ํจํค์ง(Package) ํ์ต: ๊ด๋ จ๋ ํ๋ก์์ ์ ํจ์๋ฅผ ํ๋๋ก ๋ฌถ์ด ๊ด๋ฆฌ
2๏ธโฃ ๊ณ ๊ธ ์ปค์ ํ์ฉ: REF CURSOR, ์ปค์ ๋ณ์ ๋ฑ
3๏ธโฃ ๋์ SQL ๋ง์คํฐ: EXECUTE IMMEDIATE์ ๊ณ ๊ธ ํ์ฉ
4๏ธโฃ ์ฑ๋ฅ ํ๋: ์คํ ๊ณํ ๋ถ์, ์ธ๋ฑ์ค ์ต์ ํ
5๏ธโฃ ๋ณด์ ๊ฐํ: ์ํธํ, ๊ถํ ๊ด๋ฆฌ, SQL ์ธ์ ์ ๋ฐฉ์ด
6๏ธโฃ ์ค์ ํ๋ก์ ํธ: ์ค์ ์๋น์ค๋ฅผ ๋ง๋ค์ด๋ณด๋ฉฐ ๊ฒฝํ ์๊ธฐ
๋ง์ง๋ง ์กฐ์ธ
ํ๋ก๊ทธ๋๋ฐ์ ์ด๋ก ๋ง์ผ๋ก๋ ์ ๋ ๋์ง ์์. ์ง์ ์ฝ๋๋ฅผ ์์ฑํ๊ณ , ์๋ฌ๋ฅผ ๋ง๋๊ณ , ํด๊ฒฐํ๋ ๊ณผ์ ์ ๋ฐ๋ณตํด์ผ ์ง์ง ์ค๋ ฅ์ด ์๊ฒจ. ๐ช์ฒ์์๋ ๊ฐ๋จํ ํ๋ก์์ ๋ถํฐ ์์ํด์, ์ ์ ๋ณต์กํ ๋ก์ง์ ๊ตฌํํด๋ณด์. ์๋ฌ๊ฐ ๋๋ ์ข์ ํ์ง ๋ง๊ณ , ๊ทธ๊ฒ ๋ฐ๋ก ๋ฐฐ์ฐ๋ ๊ณผ์ ์ด๋ผ๊ณ ์๊ฐํด. ๋ชจ๋ ๊ณ ์๋ค๋ ๋ค ๊ทธ ๊ณผ์ ์ ๊ฑฐ์ณค์ด! ๐
๊ทธ๋ฆฌ๊ณ ํญ์ ์ฝ๋๋ฅผ ์์ฑํ ๋๋ "๋์ค์ ์ด ์ฝ๋๋ฅผ ๋ณด๋ ์ฌ๋(๋ฏธ๋์ ๋ ํฌํจ!)์ด ์ดํดํ๊ธฐ ์ฌ์ธ๊น?"๋ฅผ ์๊ฐํ๋ฉฐ ์์ฑํ์. ๊น๋ํ ์ฝ๋, ๋ช ํํ ์ฃผ์, ์ผ๊ด๋ ์คํ์ผ์ด ์ ๋ง ์ค์ํด! ๐
ํผ์ ๊ณต๋ถํ๋ค ๋งํ๋ ๋ถ๋ถ์ด ์์ผ๋ฉด ์ฃผ์ ํ์ง ๋ง๊ณ ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํด๋ด. Oracle ๊ณต์ ๋ฌธ์, Stack Overflow, ๊ตญ๋ด ๊ฐ๋ฐ์ ์ปค๋ฎค๋ํฐ ๋ฑ ๋์๋ฐ์ ๊ณณ์ ๋ง์!
๊ทธ๋ฆฌ๊ณ ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ค์ ํ๋ก์ ํธ ๊ฒฝํ์ ์๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ์์ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ํฌํธํด๋ฆฌ์ค๋ฅผ ๋ง๋ค์ด๊ฐ๋ค ๋ณด๋ฉด, ์ด๋์ ํ๋ก ๊ฐ๋ฐ์๊ฐ ๋์ด ์์ ๊ฑฐ์ผ! ๐
๐ Oracle PL/SQL ๋ง์คํฐ๋ฅผ ํฅํ ์ฌ์ , ์์! ๐
Keep Coding, Keep Learning! ๐ช
๊ด๋ จ ํค์๋
๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.
์์ง ๋๊ธ์ด ์์ต๋๋ค.