ROWNUM/OFFSET ํ์ด์ง, SYSDATE ๋ ์ง ์ฐ์ฐ, ๊ณ์ธตํ ์ฟผ๋ฆฌ(CONNECT BY), MERGE INTO, ์ธ๋ฑ์ค ํํธ ๋ฐ ์ค๋ฌด ๋์
๋๋ฆฌ ๋ ํผ๋ฐ์ค
## 1. ํ์ด์ง ์ฟผ๋ฆฌ (Pagination: 12c+ vs ROWNUM)
```sql
-- 1. Oracle 12c ์ด์ ์ต์ ํ์ค ๋ฌธ๋ฒ (๊ถ์ฅ)
SELECT employee_id, first_name, salary
FROM employees
ORDER BY employee_id
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY; -- 21๋ฒ์งธ๋ถํฐ 10๊ฐ ์กฐํ
-- 2. Oracle 11g ์ดํ ROWNUM 3์ค ์ธ๋ผ์ธ ๋ทฐ (๋ ๊ฑฐ์ ํ์ค)
SELECT *
FROM (
SELECT a.*, ROWNUM AS rnum
FROM (
SELECT employee_id, first_name, salary
FROM employees
ORDER BY employee_id
) a
WHERE ROWNUM <= 30 -- ์ข
๋ฃ ๋ฒํธ
)
WHERE rnum >= 21; -- ์์ ๋ฒํธ
```
## 2. ๋ ์ง & ์๊ฐ ํจ์ (Date & Timestamp)
```sql
-- ํ์ฌ ์ผ์
SELECT SYSDATE, SYSTIMESTAMP FROM DUAL;
-- ๋ ์ง ํฌ๋งท ๋ณํ (TO_CHAR / TO_DATE)
SELECT TO_CHAR(SYSDATE, 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
SELECT TO_DATE('2026-09-26 14:30:00', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL;
-- ๋ ์ง ์ฐ์ฐ (๊ธฐ๋ณธ ์ผ ๋จ์)
SELECT SYSDATE + 1 AS ๋ด์ผ FROM DUAL;
SELECT SYSDATE + 1/24 AS 1์๊ฐ_ํ FROM DUAL;
SELECT SYSDATE + 10/(24*60) AS 10๋ถ_ํ FROM DUAL;
SELECT ADD_MONTHS(SYSDATE, 1) AS 1๋ฌ_ํ FROM DUAL;
SELECT LAST_DAY(SYSDATE) AS ์ด๋ฒ๋ฌ_๋ง์ผ FROM DUAL;
SELECT MONTHS_BETWEEN(SYSDATE, TO_DATE('2026-01-01', 'YYYY-MM-DD')) FROM DUAL;
SELECT TRUNC(SYSDATE, 'DD') AS ์ค๋์์ , TRUNC(SYSDATE, 'MM') AS ์ด๋ฒ๋ฌ1์ผ FROM DUAL;
```
## 3. NULL ์ฒ๋ฆฌ & ๋ถ๊ธฐ ์กฐ๊ฑด๋ฌธ
```sql
-- 1. NVL: null์ด๋ฉด ๋์ฒด๊ฐ ๋ฐํ
SELECT NVL(commission_pct, 0) FROM employees;
-- 2. NVL2(expr, not_null_val, null_val)
SELECT NVL2(commission_pct, '๋ณด๋์ค ๋์', '๊ธฐ๋ณธ๊ธ') FROM employees;
-- 3. COALESCE: null์ด ์๋ ์ฒซ ๋ฒ์งธ ๊ฐ ๋ฐํ
SELECT COALESCE(phone, mobile, email, '์ฐ๋ฝ์ฒ ์์') FROM users;
-- 4. DECODE: Oracle ์ ์ฉ if-else ๋๋ฑ ๋น๊ต
SELECT DECODE(dept_id, 10, '์์
', 20, '๊ฐ๋ฐ', '๊ธฐํ') FROM employees;
-- 5. ํ์ค CASE WHEN ๊ตฌ๋ฌธ
SELECT
CASE
WHEN salary >= 5000 THEN '๊ณ ์ก'
WHEN salary >= 3000 THEN '์ค๊ฐ'
ELSE '์ผ๋ฐ'
END AS sal_grade
FROM employees;
```
## 4. ๋ฌธ์์ด ์กฐ์ & ์ ๊ท์
```sql
-- ๋ฌธ์์ด ๊ฒฐํฉ: || ์ฐ์ฐ์ (๋๋ CONCAT ํจ์)
SELECT first_name || ' ' || last_name AS full_name FROM employees;
-- ๋ถ๋ถ ๋ฌธ์์ด & ๊ฒ์ (1๋ถํฐ ์์)
SELECT SUBSTR('ORACLE', 1, 3) FROM DUAL; -- 'ORA'
SELECT INSTR('ORACLE', 'A') FROM DUAL; -- 3 (์์น)
-- ๊ณต๋ฐฑ/ํจ๋ฉ (LPAD / RPAD)
SELECT LPAD('123', 5, '0') FROM DUAL; -- '00123' (๊ณ ์ ์๋ฆฟ์ ์ฝ๋ ์์ฑ ์)
-- ์ฌ๋ฌ ํ์ ๋ฌธ์์ด์ ํ ์ค๋ก ํฉ์น๊ธฐ (LISTAGG)
SELECT dept_id,
LISTAGG(first_name, ', ') WITHIN GROUP (ORDER BY first_name) AS names
FROM employees
GROUP BY dept_id;
```
## 5. ๊ณ์ธตํ ์ฟผ๋ฆฌ (START WITH & CONNECT BY)
```sql
-- ์กฐ์ง๋, ์นดํ
๊ณ ๋ฆฌ ํธ๋ฆฌ ๋ฑ ๊ณ์ธต ๋ฐ์ดํฐ ์กฐํ
SELECT
LEVEL, -- ๊ณ์ธต ๊น์ด (1๋ถํฐ ์์)
LPAD(' ', (LEVEL - 1) * 2) || emp_name AS name, -- ๋ค์ฌ์ฐ๊ธฐ ์๊ฐํ
emp_id,
manager_id,
SYS_CONNECT_BY_PATH(emp_name, ' > ') AS path, -- ๋ฃจํธ๋ถํฐ์ ๊ฒฝ๋ก
CONNECT_BY_ISLEAF AS is_leaf -- ์ตํ์ ์์ ๋
ธ๋ ์ฌ๋ถ (1 or 0)
FROM employees
START WITH manager_id IS NULL -- ๋ฃจํธ ๋
ธ๋ ์กฐ๊ฑด
CONNECT BY PRIOR emp_id = manager_id -- ๋ถ๋ชจ-์์ ์ฐ๊ฒฐ ๊ด๊ณ
ORDER SIBLINGS BY emp_name; -- ๊ณ์ธต ๊ตฌ์กฐ ์ ์งํ๋ฉฐ ํ์ ๊ฐ ์ ๋ ฌ
```
## 6. ์ํ์ค & DUAL & MERGE INTO
```sql
-- ์ํ์ค ์์ฑ ๋ฐ ์ฌ์ฉ
CREATE SEQUENCE user_seq START WITH 1 INCREMENT BY 1 NOCACHE;
SELECT user_seq.NEXTVAL FROM DUAL; -- ๋ฒํธ ๋ฐ๊ธ
SELECT user_seq.CURRVAL FROM DUAL; -- ํ์ฌ ๋ฒํธ
-- MERGE INTO (Upsert: ์กด์ฌํ๋ฉด ์์ , ์์ผ๋ฉด ๋ฑ๋ก)
MERGE INTO users u
USING DUAL
ON (u.user_id = :id)
WHEN MATCHED THEN
UPDATE SET u.name = :name, u.updated_at = SYSDATE
WHEN NOT MATCHED THEN
INSERT (user_id, name, created_at)
VALUES (:id, :name, SYSDATE);
```
## 7. ์๋์ฐ & ๋ถ์ ํจ์ (Window Functions)
```sql
SELECT
emp_id, dept_id, salary,
-- 1. ์์ ๋งค๊ธฐ๊ธฐ
ROW_NUMBER() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS row_num,
DENSE_RANK() OVER (PARTITION BY dept_id ORDER BY salary DESC) AS dense_rk,
-- 2. ์ด์ ํ / ๋ค์ํ ๊ฐ์ ธ์ค๊ธฐ
LAG(salary, 1, 0) OVER (PARTITION BY dept_id ORDER BY hire_date) AS prev_sal,
LEAD(salary, 1, 0) OVER (PARTITION BY dept_id ORDER BY hire_date) AS next_sal,
-- 3. ๋ถ์๋ณ ๋์ ํฉ๊ณ
SUM(salary) OVER (PARTITION BY dept_id ORDER BY hire_date) AS running_sum
FROM employees;
```
## 8. ์ตํฐ๋ง์ด์ ํํธ (Hints)
```sql
-- 1. ์ธ๋ฑ์ค ๊ฐ์ ์ฌ์ฉ
SELECT /*+ INDEX(e idx_emp_salary) */ *
FROM employees e WHERE salary > 3000;
-- 2. ํ ํ
์ด๋ธ ์ค์บ ๊ฐ์
SELECT /*+ FULL(e) */ *
FROM employees e WHERE dept_id = 10;
-- 3. ์กฐ์ธ ์์ ๋ฐ ๋ฐฉ์ ์ง์ (Nested Loops / Hash Join)
SELECT /*+ LEADING(d e) USE_NL(e) */ *
FROM departments d
JOIN employees e ON d.dept_id = e.dept_id;
-- 4. ๋ณ๋ ฌ ์ฟผ๋ฆฌ ์คํ (๋์ฉ๋ ๋ฐฐ์น ์์
)
SELECT /*+ PARALLEL(e, 4) */ COUNT(*) FROM large_table e;
```
## 9. ์คํ๊ณํ ํ์ธ (Explain Plan)
```sql
-- ์คํ๊ณํ ์์ฑ
EXPLAIN PLAN FOR
SELECT * FROM employees WHERE email = 'SKING';
-- ์คํ๊ณํ ํฌ๋งท ์ถ๋ ฅ
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY);
-- ์ค์ ์ํ ํต๊ณ(Actual Rows/Cost) ํ์ธ (GATHER_PLAN_STATISTICS ํํธ ์ฌ์ฉ ์)
SELECT /*+ GATHER_PLAN_STATISTICS */ * FROM employees WHERE dept_id = 10;
SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR(NULL, NULL, 'ALLSTATS LAST'));
```
## 10. ์ค๋ฌด ํ์ ๋ฐ์ดํฐ ๋์
๋๋ฆฌ ๋ทฐ
```sql
-- 1. ํ์ฌ ์ ์์ ๋ฐ ๋ฝ(Lock) ํ์ธ & ์ธ์
ํฌ
SELECT sid, serial#, username, status, machine, program
FROM v$session WHERE status = 'ACTIVE';
-- ์ธ์
๊ฐ์ ์ข
๋ฃ (ALTER SYSTEM KILL SESSION 'sid,serial#')
-- ALTER SYSTEM KILL SESSION '142,35123';
-- 2. ๋ด๊ฐ ๋ณด์ ํ ํ
์ด๋ธ / ์ปฌ๋ผ ๋ชฉ๋ก
SELECT table_name FROM user_tables ORDER BY table_name;
SELECT column_name, data_type, data_length, nullable
FROM user_tab_columns WHERE table_name = 'EMPLOYEES';
-- 3. ์ธ๋ฑ์ค ๋ฐ ์ ์ฝ์กฐ๊ฑด ํ์ธ
SELECT index_name, table_name, uniqueness FROM user_indexes;
SELECT constraint_name, constraint_type, table_name FROM user_constraints;
```
์๊ฒฌ ๋ฐ ์ง๋ฌธ
0์์ง ๋ฑ๋ก๋ ์๊ฒฌ์ด ์์ต๋๋ค. ์ฒซ ๋ฒ์งธ ๋๊ธ์ ๋จ๊ฒจ๋ณด์ธ์!
๋๊ธ ์์
๋๊ธ ์ญ์