본문 바로가기
Development/Database

[DB] Oracle Sequence 생성과 활용 방법 - 기본부터 성능 최적화까지

by 은스타 2020. 4. 8.
반응형
Oracle Sequence 생성과 활용 방법 - 기본부터 성능 최적화까지

Oracle Sequence 생성과 활용 방법 - 기본부터 성능 최적화까지

Oracle 데이터베이스에서 Sequence는 일련번호를 자동으로 생성해주는 데이터베이스 객체입니다. 주로 기본 키(Primary Key)나 고유 식별자를 생성하는데 사용되며, 테이블과 독립적으로 관리되어 여러 테이블에서 공유할 수 있습니다.

이번 포스팅에서는 Sequence의 기본 개념부터 생성, 수정, 삭제 방법은 물론 성능 최적화 기법과 다른 DBMS와의 비교까지 실무에서 필요한 모든 내용을 상세히 다룹니다. 특히 CACHE 설정에 따른 성능 차이와 RAC 환경에서의 주의사항을 중점적으로 설명합니다.

목차
1. Sequence의 개념과 특징
2. Sequence 생성 방법
3. Sequence 사용 및 조회
4. Sequence 수정과 삭제
5. 자주 묻는 질문 (FAQ)


#1. Sequence의 개념과 특징

Sequence는 Oracle 데이터베이스에서 제공하는 자동 번호 생성 객체로, 애플리케이션 레벨에서 MAX+1 방식으로 채번하는 것보다 훨씬 효율적이고 안전합니다.

1) Sequence란 무엇인가

Sequence는 일련의 숫자를 자동으로 생성해주는 독립적인 데이터베이스 객체입니다. 테이블, 뷰, 인덱스와 같은 스키마 객체의 한 종류이며, 특정 테이블에 종속되지 않아 여러 테이블에서 공유할 수 있습니다.

(1) Sequence의 주요 특징
① 유일한 숫자 값 자동 생성: 동시성 제어를 통해 중복 없는 번호 보장
② 공유 가능한 객체: 여러 테이블에서 동일한 Sequence 사용 가능
③ 독립적인 관리: 테이블과 별도로 생성, 수정, 삭제 가능
④ 메모리 캐싱: Shared Pool에 캐싱되어 빠른 접근 속도 제공
⑤ 유연한 설정: 증감 간격, 최대/최소값, 순환 여부 등 다양한 옵션 지원
. . . . .
2) Sequence vs 다른 채번 방식

ID 값을 생성하는 방법은 여러 가지가 있습니다. 각 방식의 장단점을 비교해보겠습니다.

채번 방식 장점 단점 적합한 경우
Sequence 빠른 성능, 동시성 보장, 관리 용이 번호 중간에 갭(gap) 발생 가능 대부분의 경우 권장
MAX+1 방식 갭 없는 연속 번호 성능 저하, 동시성 문제, Lock 경합 거래번호 등 갭이 허용되지 않는 경우
테이블 방식 완벽한 제어 가능 구현 복잡, 성능 저하 특수한 비즈니스 로직이 필요한 경우
GUID/UUID 완전한 유일성, 분산 환경 적합 16바이트 크기, 가독성 낮음 글로벌 분산 시스템
. . . . .
3) Sequence의 동작 원리

Sequence는 메모리(Shared Pool)에 캐싱되어 데이터 딕셔너리 접근을 최소화합니다. 이것이 Sequence가 빠른 핵심 이유입니다.

(1) CACHE 동작 방식

CACHE 20으로 설정된 Sequence의 동작 과정을 살펴보겠습니다.

① 첫 번째 NEXTVAL 호출 시 SEQ$ 테이블에서 1~20까지 예약
② 메모리에 1~20 범위를 캐싱
③ 2~20번 NEXTVAL 호출은 메모리에서만 처리 (디스크 I/O 없음)
④ 21번째 호출 시 다시 SEQ$ 테이블 접근하여 21~40 예약

이 방식으로 데이터 딕셔너리 업데이트 횟수를 1/20로 줄여 성능을 크게 향상시킵니다.

(2) NOCACHE의 성능 문제

NOCACHE로 설정하면 매번 SEQ$ 테이블을 업데이트해야 합니다. 초당 1,000,000개의 시퀀스를 사용하는 시스템에서 CACHE 20이면 초당 50,000번의 데이터 딕셔너리 업데이트가 발생합니다.


#2. Sequence 생성 방법

Sequence를 생성하는 CREATE SEQUENCE 명령은 다양한 옵션을 제공하여 비즈니스 요구사항에 맞게 설정할 수 있습니다.

1) 기본 구문과 옵션
CREATE SEQUENCE 시퀀스_이름
    [INCREMENT BY n]
    [START WITH n]
    [MAXVALUE n | NOMAXVALUE]
    [MINVALUE n | NOMINVALUE]
    [CYCLE | NOCYCLE]
    [CACHE n | NOCACHE]
    [ORDER | NOORDER];
옵션 설명 기본값
INCREMENT BY n 시퀀스 증감 값 (양수는 증가, 음수는 감소) 1
START WITH n 시퀀스 시작 값 오름차순: 1, 내림차순: -1
MAXVALUE n 최대값 지정 오름차순: 10^27
MINVALUE n 최소값 지정 오름차순: 1
CYCLE 최대/최소값 도달 시 반대쪽 값부터 재시작 NOCYCLE
CACHE n 메모리에 미리 할당할 시퀀스 개수 20
ORDER 요청 순서대로 번호 생성 보장 (RAC 환경) NOORDER
. . . . .
2) 다양한 Sequence 생성 예제
(1) 가장 간단한 Sequence
-- 1부터 시작하여 1씩 증가하는 기본 시퀀스
CREATE SEQUENCE employee_seq;

모든 옵션이 기본값으로 설정됩니다. 실무에서는 명시적으로 옵션을 지정하는 것이 좋습니다.

(2) 실무용 표준 Sequence
-- 주문번호용 시퀀스 (1000부터 10씩 증가)
CREATE SEQUENCE order_seq
    INCREMENT BY 10
    START WITH 1000
    MAXVALUE 99999999
    NOCYCLE
    CACHE 100
    NOORDER;

-- 고객 ID용 시퀀스 (성능 최적화)
CREATE SEQUENCE customer_id_seq
    START WITH 1
    INCREMENT BY 1
    NOMAXVALUE
    NOCYCLE
    CACHE 1000
    NOORDER;
(3) 감소하는 Sequence
-- 카운트다운용 시퀀스
CREATE SEQUENCE countdown_seq
    INCREMENT BY -1
    START WITH 100
    MINVALUE 1
    MAXVALUE 100
    CYCLE
    NOCACHE;
(4) RAC 환경용 Sequence
-- RAC에서 순서가 보장되어야 하는 경우
CREATE SEQUENCE transaction_seq
    START WITH 1
    INCREMENT BY 1
    CACHE 10000
    ORDER; -- 순서 보장 (성능 저하 있음)
. . . . .
3) CACHE 크기 결정 가이드

CACHE 크기는 시스템 트랜잭션 볼륨에 따라 결정해야 합니다.

초당 INSERT 횟수 권장 CACHE 크기 이유
~100 20 (기본값) 기본값으로 충분
100~1,000 100~500 적절한 균형점
1,000~10,000 1,000~5,000 데이터 딕셔너리 부하 감소
10,000 이상 10,000~100,000 고성능 환경 최적화

CACHE 크기 계산 공식: CACHE = 초당 INSERT 수 × 예상 지속 시간(초)

. . . . .
4) 기존 테이블 기반 Sequence 생성

기존 데이터가 있는 테이블에 Sequence를 추가할 때는 현재 최대값보다 큰 값에서 시작해야 합니다.

-- 기존 테이블의 최대 ID 조회 후 Sequence 생성
DECLARE
    v_max_id NUMBER;
BEGIN
    -- 최대 ID 값 조회
    SELECT NVL(MAX(employee_id), 0) INTO v_max_id
    FROM employees;
    
    -- 기존 시퀀스 삭제 (있을 경우)
    BEGIN
        EXECUTE IMMEDIATE 'DROP SEQUENCE emp_id_seq';
    EXCEPTION
        WHEN OTHERS THEN NULL;
    END;
    
    -- 최대값 + 1부터 시작하는 시퀀스 생성
    EXECUTE IMMEDIATE
        'CREATE SEQUENCE emp_id_seq ' ||
        'START WITH ' || (v_max_id + 1) ||
        ' INCREMENT BY 1 ' ||
        'CACHE 100 NOCYCLE';
    
    DBMS_OUTPUT.PUT_LINE('Sequence created starting from: ' || (v_max_id + 1));
END;
/

#3. Sequence 사용 및 조회

생성된 Sequence를 사용하는 방법과 정보를 조회하는 방법을 알아보겠습니다.

1) NEXTVAL과 CURRVAL

Sequence에서 값을 가져오는 두 가지 의사컬럼(Pseudo Column)이 있습니다.

(1) NEXTVAL - 다음 값 생성
-- 다음 시퀀스 값 조회
SELECT employee_seq.NEXTVAL FROM dual;

-- INSERT 문에서 사용
INSERT INTO employees (employee_id, first_name, last_name)
VALUES (employee_seq.NEXTVAL, 'John', 'Doe');

-- UPDATE 문에서 사용
UPDATE temp_table
SET id = employee_seq.NEXTVAL
WHERE id IS NULL;
(2) CURRVAL - 현재 값 조회

CURRVAL은 같은 세션에서 NEXTVAL을 최소 한 번 호출한 후에만 사용 가능합니다.

-- 현재 시퀀스 값 조회
SELECT employee_seq.CURRVAL FROM dual;

-- 여러 테이블에 동일한 ID 삽입
INSERT INTO employees (employee_id, name)
VALUES (employee_seq.NEXTVAL, 'Alice');

INSERT INTO employee_log (log_id, employee_id, action)
VALUES (log_seq.NEXTVAL, employee_seq.CURRVAL, 'INSERT');
. . . . .
2) Sequence 사용 제약사항

Sequence는 모든 SQL 구문에서 사용할 수 있는 것은 아닙니다.

사용 가능한 위치 사용 불가능한 위치
✓ SELECT 문의 SELECT 목록 ✗ VIEW의 SELECT 문
✓ INSERT 문의 VALUES 절 ✗ DISTINCT가 있는 SELECT 문
✓ UPDATE 문의 SET 절 ✗ GROUP BY, HAVING, ORDER BY 절
✓ 서브쿼리의 SELECT 목록 (일부) ✗ CHECK 제약조건
✓ MERGE 문 ✗ DEFAULT 값 (11g 이전)
. . . . .
3) Sequence 정보 조회

데이터 딕셔너리 뷰를 통해 Sequence 정보를 확인할 수 있습니다.

(1) 사용자 소유 Sequence 조회
-- 현재 사용자의 모든 시퀀스 목록
SELECT sequence_name,
       min_value,
       max_value,
       increment_by,
       last_number,
       cache_size,
       cycle_flag
FROM user_sequences
ORDER BY sequence_name;
(2) 특정 Sequence 상세 정보
-- 특정 시퀀스의 상세 정보 조회
SELECT *
FROM user_sequences
WHERE sequence_name = 'EMPLOYEE_SEQ';

-- DBA 권한으로 모든 시퀀스 조회
SELECT sequence_owner,
       sequence_name,
       last_number,
       cache_size
FROM dba_sequences
WHERE sequence_owner = 'HR'
ORDER BY last_number DESC;
. . . . .
4) 트리거와 함께 사용하기

트리거를 사용하면 INSERT 시 자동으로 ID를 할당할 수 있습니다.

-- 시퀀스 생성
CREATE SEQUENCE invoice_seq
    START WITH 10001
    INCREMENT BY 1
    CACHE 100;

-- 자동 ID 할당 트리거
CREATE OR REPLACE TRIGGER trg_invoice_id
BEFORE INSERT ON invoices
FOR EACH ROW
BEGIN
    IF :NEW.invoice_id IS NULL THEN
        :NEW.invoice_id := invoice_seq.NEXTVAL;
    END IF;
END;
/

-- 이제 invoice_id 없이 INSERT 가능
INSERT INTO invoices (customer_id, amount, invoice_date)
VALUES (1001, 599.99, SYSDATE);

#4. Sequence 수정과 삭제

생성된 Sequence는 필요에 따라 수정하거나 삭제할 수 있습니다.

1) Sequence 수정 (ALTER SEQUENCE)

ALTER SEQUENCE 명령으로 대부분의 옵션을 변경할 수 있지만, START WITH 값은 변경할 수 없습니다.

ALTER SEQUENCE 시퀀스_이름
    [INCREMENT BY n]
    [MAXVALUE n | NOMAXVALUE]
    [MINVALUE n | NOMINVALUE]
    [CYCLE | NOCYCLE]
    [CACHE n | NOCACHE]
    [ORDER | NOORDER];
(1) 증분 값 변경
-- 1씩 증가하던 것을 5씩 증가로 변경
ALTER SEQUENCE employee_seq INCREMENT BY 5;
(2) 캐시 크기 변경
-- 성능 최적화를 위해 캐시 크기 증가
ALTER SEQUENCE order_seq
    CACHE 5000;
(3) 순환 옵션 추가
-- 최대값 도달 시 재사용하도록 변경
ALTER SEQUENCE countdown_seq CYCLE;
. . . . .
2) Sequence 값 재설정

START WITH는 변경할 수 없으므로 현재 값을 특정 값으로 재설정하려면 우회 방법을 사용해야 합니다.

-- 시퀀스를 5000으로 재설정하는 프로시저
DECLARE
    v_current_val NUMBER;
    v_desired_val NUMBER := 5000;
    v_increment NUMBER;
BEGIN
    -- 현재 값 조회
    SELECT seq_name.NEXTVAL INTO v_current_val FROM dual;
    
    -- 목표 값과의 차이 계산
    v_increment := v_desired_val - v_current_val;
    
    -- INCREMENT BY를 차이만큼 변경
    EXECUTE IMMEDIATE
        'ALTER SEQUENCE seq_name INCREMENT BY ' || v_increment;
    
    -- 한 번 더 NEXTVAL 호출하여 목표 값으로 이동
    SELECT seq_name.NEXTVAL INTO v_current_val FROM dual;
    
    -- INCREMENT BY를 원래대로 복원
    EXECUTE IMMEDIATE
        'ALTER SEQUENCE seq_name INCREMENT BY 1';
    
    DBMS_OUTPUT.PUT_LINE('Sequence reset to: ' || v_current_val);
END;
/
. . . . .
3) Sequence 삭제 (DROP SEQUENCE)
-- 기본 삭제
DROP SEQUENCE employee_seq;

-- Oracle 12c 이상: 존재할 때만 삭제
DROP SEQUENCE IF EXISTS temp_seq;

-- 11g 이하: PL/SQL로 조건부 삭제
BEGIN
    EXECUTE IMMEDIATE 'DROP SEQUENCE temp_seq';
EXCEPTION
    WHEN OTHERS THEN
        IF SQLCODE != -2289 THEN -- ORA-02289: sequence does not exist
            RAISE;
        END IF;
END;
/
. . . . .
4) 다른 DBMS와의 비교

Oracle 외의 주요 DBMS에서 Sequence를 사용하는 방법입니다.

(1) MySQL/MariaDB
-- MySQL은 AUTO_INCREMENT 사용 (8.0 이전)
CREATE TABLE customers (
    customer_id INT AUTO_INCREMENT PRIMARY KEY,
    customer_name VARCHAR(100)
);

-- MySQL 8.0부터 Sequence 지원
CREATE SEQUENCE customer_seq
START WITH 1000
INCREMENT BY 1;

SELECT NEXT VALUE FOR customer_seq;
(2) PostgreSQL
-- PostgreSQL Sequence
CREATE SEQUENCE customer_seq
INCREMENT BY 1
START WITH 1000
NO MAXVALUE
NO CYCLE
CACHE 20;

-- 사용 방법
SELECT nextval('customer_seq');
SELECT currval('customer_seq');
(3) SQL Server
-- SQL Server 2012 이상
CREATE SEQUENCE customer_seq
START WITH 1000
INCREMENT BY 1
MINVALUE 1000
MAXVALUE 9999999
CYCLE
CACHE 100;

-- 사용 방법
SELECT NEXT VALUE FOR customer_seq;

#5. 자주 묻는 질문 (FAQ)
1) Q: 시퀀스 값이 갑자기 건너뛰는 이유는 무엇인가요?

A: 시퀀스 값이 건너뛰는 주요 원인은 다음과 같습니다.

캐싱으로 인한 손실: 인스턴스가 비정상 종료되면 캐시된 값이 손실됩니다. CACHE 100으로 설정하고 50번 사용 후 shutdown abort하면 51~100이 손실되어 다음 값은 101부터 시작합니다.
② 롤백: 트랜잭션이 롤백되어도 시퀀스 값은 롤백되지 않습니다. 이는 동시성을 위한 의도적인 설계입니다.
③ 세션 종료: 세션이 비정상 종료되면 할당된 캐시 값이 손실됩니다.
④ ALTER SEQUENCE: 증분 값을 변경하면 현재 캐시가 무효화됩니다.

갭이 허용되지 않는 경우 NOCACHE를 사용하거나 테이블 기반 채번 방식을 고려해야 합니다.

. . . . .
2) Q: CURRVAL 사용 시 "ORA-08002" 오류가 발생합니다.

A: CURRVAL은 같은 세션에서 NEXTVAL을 최소 한 번 호출한 후에만 사용 가능합니다. 새 세션을 시작했거나 NEXTVAL을 한 번도 호출하지 않았다면 이 오류가 발생합니다.

-- 오류 발생
SELECT employee_seq.CURRVAL FROM dual;
-- ORA-08002: sequence EMPLOYEE_SEQ.CURRVAL is not yet defined in this session

-- 올바른 사용법
SELECT employee_seq.NEXTVAL FROM dual; -- 먼저 NEXTVAL 호출
SELECT employee_seq.CURRVAL FROM dual; -- 이제 사용 가능
. . . . .
3) Q: CACHE vs NOCACHE, 어떤 것을 선택해야 하나요?

A: 대부분의 경우 CACHE 사용을 강력히 권장합니다.

구분 CACHE NOCACHE
성능 매우 빠름 (메모리 접근) 느림 (매번 디스크 I/O)
갭 발생 가능 (인스턴스 종료 시) 롤백 시에만 발생
동시성 우수 Lock 경합 발생
권장 사용 일반적인 모든 경우 갭이 절대 허용되지 않는 경우

실제 벤치마크 결과, CACHE 50000으로 50,000개의 시퀀스를 가져오는 데 16초가 걸린 반면, NOCACHE는 58초가 소요되어 약 3.6배의 성능 차이를 보였습니다.

. . . . .
4) Q: RAC 환경에서 ORDER vs NOORDER의 차이는 무엇인가요?

A: ORDER는 여러 인스턴스에서 시퀀스 값의 순서를 보장하지만 성능이 저하됩니다.

① NOORDER (기본값): 각 인스턴스가 독립적으로 캐시 범위를 할당받아 성능이 좋지만, 전체적으로 순서가 보장되지 않을 수 있습니다.
② ORDER: 인스턴스 간 통신으로 순서를 보장하여 성능이 저하됩니다.

단일 인스턴스 환경에서는 ORDER 옵션이 의미가 없으므로 NOORDER를 사용하세요.

. . . . .
5) Q: 시퀀스 성능을 최적화하는 방법은 무엇인가요?

A: 성능 최적화를 위한 체크리스트입니다.

적절한 CACHE 크기 설정: 초당 INSERT 수에 따라 100~10,000 범위에서 설정
② NOORDER 사용: RAC가 아니거나 순서가 중요하지 않으면 NOORDER 사용
③ AWR 리포트 확인: "SQL ordered by CPU Time"에서 SEQ$ 업데이트 쿼리가 상위에 있으면 캐시 증가
④ Oracle 19.10 이상: 자동 캐시 조정 기능 활용
-- 성능이 중요한 고트래픽 시퀀스
CREATE SEQUENCE high_volume_seq
    START WITH 1
    INCREMENT BY 1
    CACHE 10000 -- 대용량 캐시
    NOORDER; -- 순서 보장 불필요
. . . . .
6) Q: 갭 없는 연속 번호가 필요한 경우는 어떻게 하나요?

A: 시퀀스는 기본적으로 갭이 발생할 수 있도록 설계되었습니다. 갭 없는 번호가 법적으로 요구되는 경우 다음 방법을 고려하세요.

-- 테이블 기반 갭 없는 시퀀스
CREATE TABLE gapless_sequence (
    sequence_name VARCHAR2(30) PRIMARY KEY,
    current_value NUMBER NOT NULL
);

INSERT INTO gapless_sequence VALUES ('INVOICE_SEQ', 0);

-- 다음 값 얻기 함수
CREATE OR REPLACE FUNCTION get_next_gapless_value(
    p_seq_name VARCHAR2
) RETURN NUMBER IS
    v_value NUMBER;
BEGIN
    UPDATE gapless_sequence
    SET current_value = current_value + 1
    WHERE sequence_name = p_seq_name
    RETURNING current_value INTO v_value;
    
    COMMIT;
    RETURN v_value;
END;
/

단, 이 방식은 성능 저하와 Lock 경합이 발생하므로 정말 필요한 경우에만 사용하세요.

. . . . .
7) Q: 주문번호처럼 문자와 숫자를 조합한 형식을 만들 수 있나요?

A: 시퀀스는 숫자만 생성하지만, 함수나 트리거를 사용하여 포맷을 적용할 수 있습니다.

-- 주문번호 형식 생성 함수
CREATE OR REPLACE FUNCTION generate_order_number
RETURN VARCHAR2
IS
    v_order_num VARCHAR2(20);
BEGIN
    v_order_num := 'ORD-' ||
                   TO_CHAR(SYSDATE, 'YYYYMMDD') || '-' ||
                   LPAD(order_num_seq.NEXTVAL, 5, '0');
    RETURN v_order_num;
END;
/

-- 사용 예: ORD-20241217-00001
INSERT INTO orders (order_id, customer_id, order_date)
VALUES (generate_order_number(), 1001, SYSDATE);
. . . . .
8) Q: Sequence를 사용하는 테이블을 삭제하면 Sequence도 함께 삭제되나요?

A: 아니요, Sequence는 테이블과 독립적인 객체이므로 테이블을 삭제해도 Sequence는 남아있습니다. 명시적으로 DROP SEQUENCE를 실행해야 삭제됩니다.

. . . . .
9) Q: 여러 테이블에서 하나의 Sequence를 공유할 수 있나요?

A: 네, Sequence는 여러 테이블에서 공유 가능합니다. 전체 시스템에서 유일한 ID가 필요한 경우 유용합니다.

-- 공통 ID 시퀀스
CREATE SEQUENCE global_id_seq START WITH 1 CACHE 1000;

-- 여러 테이블에서 공유
INSERT INTO customers VALUES (global_id_seq.NEXTVAL, 'Alice');
INSERT INTO suppliers VALUES (global_id_seq.NEXTVAL, 'ABC Corp');
INSERT INTO products VALUES (global_id_seq.NEXTVAL, 'Widget');
. . . . .
10) Q: Sequence 권한은 어떻게 관리하나요?

A: Sequence도 다른 객체와 마찬가지로 권한 관리가 필요합니다.

-- 다른 사용자에게 Sequence 사용 권한 부여
GRANT SELECT ON employee_seq TO hr_user;

-- Sequence 수정 권한 부여
GRANT ALTER ON employee_seq TO hr_admin;

-- 권한 회수
REVOKE SELECT ON employee_seq FROM hr_user;

마무리

Oracle Sequence는 데이터베이스에서 유일한 번호를 효율적으로 생성하는 필수 객체입니다. 애플리케이션 레벨에서 MAX+1 방식을 사용하는 것보다 훨씬 빠르고 안전하며, 동시성 문제도 자동으로 해결해줍니다.

성능 최적화의 핵심은 적절한 CACHE 크기 설정입니다. 초당 트랜잭션 볼륨에 따라 CACHE 값을 조정하면 데이터 딕셔너리 업데이트 횟수를 크게 줄여 성능을 향상시킬 수 있습니다. 특히 고트래픽 시스템에서는 CACHE 값을 1,000~10,000으로 설정하는 것이 일반적입니다.

주의할 점은 다음과 같습니다.

① 시퀀스는 기본적으로 갭이 발생할 수 있도록 설계되어 있습니다
② CURRVAL은 같은 세션에서 NEXTVAL을 먼저 호출해야 사용 가능합니다
③ START WITH 값은 ALTER로 변경할 수 없습니다
④ RAC 환경에서는 ORDER 옵션 사용 여부를 신중히 결정해야 합니다

트리거와 함께 사용하면 INSERT 시 자동으로 ID를 할당할 수 있어 개발 생산성이 향상됩니다. 또한 함수를 활용하면 "ORD-20241217-00001"과 같은 포맷팅된 번호도 생성할 수 있습니다.

실무에서는 갭이 허용되는 대부분의 경우 Sequence를 사용하고, 법적으로 연속 번호가 요구되는 경우에만 테이블 기반 방식을 고려하는 것이 좋습니다.

긴 글 읽어주셔서 감사합니다.

끝.
반응형