
데이터를 실제로 저장하는 공간. 행(Row)과 열(Column)로 구성된 엑셀 시트 같은 것. DB의 핵심 객체야.
테이블을 기반으로 만든 "가상 테이블". 실제 데이터를 저장하지 않고, SELECT 쿼리를 저장해둔 것. 복잡한 쿼리를 단순하게 재사용하거나, 특정 컬럼만 보여주고 싶을 때 씀.
테이블 검색 속도를 빠르게 해주는 색인. 책의 목차 같은 개념. WHERE 조건으로 자주 조회하는 컬럼에 걸어두면 쿼리 성능이 올라감. 단, 쓰기(INSERT/UPDATE/DELETE) 성능은 약간 떨어질 수 있어.
관련된 프로시저, 함수, 변수 등을 하나로 묶어놓은 단위. 명세(Spec)와 본문(Body)으로 나뉘어져 있어. Java의 클래스 묶음(패키지)이랑 비슷한 느낌.
SQL + 로직을 묶어서 이름 붙여 저장한 것. 호출하면 실행됨. 반환값 없이 작업(INSERT, UPDATE 등)을 수행하는 게 주 목적. EXEC 프로시저명() 으로 실행.
프로시저와 비슷하지만 반드시 값을 반환(RETURN)해야 함. SELECT 문 안에서도 사용 가능. 예: SELECT get_salary(emp_id) FROM dual;
자동 증가 숫자를 생성하는 객체. MySQL의 AUTO_INCREMENT 같은 거야. PK 값 채울 때 많이 씀.
SELECT my_seq.NEXTVAL FROM dual;
특정 이벤트(INSERT, UPDATE, DELETE)가 발생했을 때 자동으로 실행되는 코드. 예: 데이터 변경 이력 자동 기록, 유효성 검사. 남용하면 디버깅이 어려워져서 꼭 필요한 곳에만 쓰는 게 좋아.
객체에 별명을 붙여주는 것. 예를 들어 SCOTT.EMP_TABLE 을 그냥 EMP로 접근하게 해줌. 공용 동의어는 모든 사용자가 접근 가능. 긴 스키마 경로 숨기거나 단순화할 때 씀.
일반 뷰와 달리 실제 데이터를 저장하는 뷰. 복잡한 집계 쿼리 결과를 미리 저장해두고 빠르게 조회할 때 씀. 대신 데이터 동기화(Refresh) 설정이 필요해.
특정 시간이나 주기에 프로시저나 작업을 자동 실행하도록 예약하는 기능. Linux의 cron과 비슷한 개념. 배치 작업, 정기 데이터 처리에 씀.
DROP TABLE로 삭제한 테이블이 바로 사라지지 않고 여기 들어와. FLASHBACK TABLE 테이블명 TO BEFORE DROP;으로 복구 가능. 실수로 날렸을 때 구원받을 수 있어.
다른 Oracle DB에 원격으로 접속해서 쿼리를 날릴 수 있는 연결 통로. 여러 DB를 연동할 때 씀. SELECT * FROM 테이블@링크명;
사용자 정의 데이터 타입. 객체 타입이나 컬렉션(배열처럼 쓰는 테이블 타입)을 만들 때 씀. 프로시저/함수에서 복잡한 데이터 구조 다룰 때 등장해.
SQL을 직접 타이핑해서 실행하는 공간. 개발자가 가장 많이 쓰는 곳이야. F5로 전체 실행, Ctrl+Enter로 커서 위치 쿼리 실행. 자유롭게 쿼리를 작성하고 결과를 볼 수 있어.
SQL을 코드로 직접 안 짜도 되는 GUI 도구. 테이블을 드래그해서 올려놓고, 조인 조건이나 필터를 클릭으로 설정하면 자동으로 SELECT 쿼리를 생성해줘. SQL이 익숙하지 않은 사람이나 쿼리 구조를 시각적으로 확인하고 싶을 때 유용해.
워크시트 - SQL 작성/실행의 기본 공간
테이블 - 모든 것의 기반
뷰 - 복잡한 쿼리 재사용
시퀀스 - PK 자동 증가
인덱스 - 성능 튜닝할 때
프로시저 / 함수 - 비즈니스 로직 구현
트리거 - 자동화 처리
패키지 - 프로시저/함수 묶어서 관리할 때
동의어 - 스키마 복잡할 때
스케줄러 - 배치 작업
구체화된 뷰 - 성능 최적화
휴지통 - 실수로 DROP 했을 때
DB 링크 - 멀티 DB 환경
-- 생성
CREATE TABLE users (
user_id NUMBER PRIMARY KEY,
name VARCHAR2(50) NOT NULL,
email VARCHAR2(100),
created_at DATE DEFAULT SYSDATE
);
-- 조회
SELECT * FROM users;
SELECT * FROM users WHERE user_id = 1;
-- 삽입
INSERT INTO users (user_id, name, email) VALUES (1, '오승연', 'sy@email.com');
-- 수정
UPDATE users SET email = 'new@email.com' WHERE user_id = 1;
-- 삭제
DELETE FROM users WHERE user_id = 1;
-- 생성
CREATE SEQUENCE user_seq
START WITH 1
INCREMENT BY 1
NOCACHE;
-- 사용 (INSERT할 때 PK에 넣음)
INSERT INTO users (user_id, name, email)
VALUES (user_seq.NEXTVAL, '오승연', 'sy@email.com');
-- 현재 값 확인
SELECT user_seq.CURRVAL FROM dual;
-- 테이블이 2개 있다고 가정
-- users: user_id, name
-- orders: order_id, user_id, product, amount
-- 복잡한 조인 쿼리를 뷰로 저장
CREATE VIEW v_user_orders AS
SELECT u.name,
o.product,
o.amount
FROM users u
JOIN orders o ON u.user_id = o.user_id;
-- 이후엔 뷰를 테이블처럼 간단하게 조회
SELECT * FROM v_user_orders;
SELECT * FROM v_user_orders WHERE name = '오승연';
-- email로 검색을 자주 한다면 인덱스 생성
CREATE INDEX idx_users_email ON users(email);
-- 이제 아래 쿼리가 훨씬 빨라짐
SELECT * FROM users WHERE email = 'sy@email.com';
-- 인덱스 확인
SELECT index_name, column_name
FROM user_ind_columns
WHERE table_name = 'USERS';
-- 특정 유저의 이메일을 업데이트하는 프로시저
CREATE OR REPLACE PROCEDURE update_email(
p_user_id IN NUMBER,
p_email IN VARCHAR2
) IS
BEGIN
UPDATE users SET email = p_email WHERE user_id = p_user_id;
COMMIT;
DBMS_OUTPUT.PUT_LINE('업데이트 완료');
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
DBMS_OUTPUT.PUT_LINE('에러: ' || SQLERRM);
END;
-- 실행
EXEC update_email(1, 'new@email.com');
-- user_id를 받아서 이름을 반환하는 함수
CREATE OR REPLACE FUNCTION get_user_name(
p_user_id IN NUMBER
) RETURN VARCHAR2 IS
v_name VARCHAR2(50);
BEGIN
SELECT name INTO v_name FROM users WHERE user_id = p_user_id;
RETURN v_name;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN '없는 유저';
END;
-- SELECT 안에서 바로 사용 가능
SELECT get_user_name(1) FROM dual;
SELECT order_id, get_user_name(user_id) AS 유저명 FROM orders;
-- users 테이블 변경 이력을 자동으로 기록
CREATE TABLE users_log (
log_id NUMBER,
user_id NUMBER,
action VARCHAR2(10), -- INSERT / UPDATE / DELETE
log_time DATE DEFAULT SYSDATE
);
CREATE OR REPLACE TRIGGER trg_users_log
AFTER INSERT OR UPDATE OR DELETE ON users
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO users_log (log_id, user_id, action)
VALUES (log_seq.NEXTVAL, :NEW.user_id, 'INSERT');
ELSIF UPDATING THEN
INSERT INTO users_log (log_id, user_id, action)
VALUES (log_seq.NEXTVAL, :NEW.user_id, 'UPDATE');
ELSIF DELETING THEN
INSERT INTO users_log (log_id, user_id, action)
VALUES (log_seq.NEXTVAL, :OLD.user_id, 'DELETE');
END IF;
END;
-- 이제 users 테이블에 변경이 생길 때마다 자동으로 로그가 쌓임