CREATE TABLE 문
CREATE TABLE 문 (CREATE TABLE Statement)
CREATE TABLE 문은 카탈로그에 테이블을 생성해요. 테이블은 데이터를 행과 열로 저장하는 기본 구조예요. 열 타입부터 제약 조건, 기본값까지 다양하게 지정할 수 있으니 예시를 통해 하나씩 살펴볼게요.
출처: 문서
본문
Examples
두 개의 정수 열(i와 j)을 가진 테이블 생성:
CREATE TABLE t1 (i INTEGER, j INTEGER);
primary key를 가진 테이블 생성:
CREATE TABLE t1 (id INTEGER PRIMARY KEY, j VARCHAR);
복합 primary key를 가진 테이블 생성:
CREATE TABLE t1 (id INTEGER, j VARCHAR, PRIMARY KEY (id, j));
다양한 타입·제약 조건·기본값을 가진 테이블 생성:
CREATE TABLE t1 (
i INTEGER NOT NULL DEFAULT 0,
decimalnr DOUBLE CHECK (decimalnr < 10),
date DATE UNIQUE,
time TIMESTAMP
);
CREATE TABLE ... AS SELECT(CTAS)로 테이블 생성:
CREATE TABLE t1 AS
SELECT 42 AS i, 84 AS j;
CSV 파일에서 테이블 생성(열 이름과 타입 자동 감지):
CREATE TABLE t1 AS
SELECT *
FROM read_csv('path/file.csv');
FROM-first 문법으로 SELECT *를 생략할 수 있어요.
CREATE TABLE t1 AS
FROM read_csv('path/file.csv');
t2의 스키마를 t1로 복사:
CREATE TABLE t1 AS
FROM t2
LIMIT 0;
열 이름과 타입만 t1로 복사되고, 다른 정보(인덱스, 제약 조건, 기본값 등)는 복사되지 않아요.
임시 테이블 (Temporary Tables)
임시 테이블은 세션 범위예요. 즉 그것을 만든 특정 커넥션만 접근할 수 있고, DuckDB로의 커넥션이 닫히면 자동으로 삭제돼요(예: PostgreSQL과 유사).
임시 테이블은 CREATE TEMP TABLE 또는 CREATE TEMPORARY TABLE 문(아래 다이어그램 참고)으로 만들 수 있고, temp.main 스키마의 일부예요. 이름은 권장되진 않지만 일반 데이터베이스 테이블의 이름과 겹칠 수 있어요. 이런 경우 임시 테이블이 이름 해석에서 우선하고, 일반 테이블을 참조하려면 memory.main.t1처럼 전체 자격이 필요해요.
임시 테이블은 영구 DuckDB에 연결해도 디스크가 아니라 메모리에 존재해요. 다만 temp_directory [설정]({% link docs/current/configuration/overview.md %})이 설정되어 있으면 메모리가 부족해질 때 데이터가 디스크로 넘칠(spill) 수 있어요.
CSV 파일에서 임시 테이블 생성(열 이름과 타입 자동 감지):
CREATE TEMP TABLE t1 AS
SELECT *
FROM read_csv('path/file.csv');
임시 테이블이 과도한 메모리를 디스크로 내려 보낼 수 있게 허용:
SET temp_directory = '/path/to/directory/';
CREATE OR REPLACE
CREATE OR REPLACE 문법은 새 테이블을 만들거나 기존 테이블을 새 테이블로 덮어쓸 수 있게 해줘요. 이는 기존 테이블을 삭제하고 새 테이블을 만드는 것의 줄임 표현이에요.
t1이 이미 있어도 두 정수 열(i와 j)을 가진 테이블 생성:
CREATE OR REPLACE TABLE t1 (i INTEGER, j INTEGER);
IF NOT EXISTS
IF NOT EXISTS 문법은 테이블이 아직 없을 때만 생성 절차를 진행해요. 이미 있으면 아무 작업도 하지 않고 기존 테이블이 데이터베이스에 남아요.
t1이 아직 없을 때만 두 정수 열(i와 j)을 가진 테이블 생성:
CREATE TABLE IF NOT EXISTS t1 (i INTEGER, j INTEGER);
CREATE TABLE ... AS SELECT (CTAS)
DuckDB는 "CTAS"로 알려진 CREATE TABLE ... AS SELECT 문법을 지원해요.
CREATE TABLE nums AS
SELECT i
FROM range(0, 3) t(i);
이 문법은 [CSV reader]({% link docs/current/data/csv/overview.md %}), 함수 지정 없이 CSV 파일에서 직접 읽는 약식, [FROM-first 문법]({% link docs/current/sql/query_syntax/from.md %}), [HTTP(S) 지원]({% link docs/current/core_extensions/httpfs/https.md %})과 함께 사용할 수 있어서 다음처럼 간결한 SQL 명령이 만들어져요.
CREATE TABLE flights AS
FROM 'https://duckdb.org/data/flights.csv';
CTAS 구성은 OR REPLACE 수정자와도 동작해서 CREATE OR REPLACE TABLE ... AS 문을 만들어요.
CREATE OR REPLACE TABLE flights AS
FROM 'https://duckdb.org/data/flights.csv';
스키마 복사
테이블의 스키마(열 이름과 타입만) 사본은 다음과 같이 만들 수 있어요.
CREATE TABLE t1 AS
FROM t2
WITH NO DATA;
또는:
CREATE TABLE t1 AS
FROM t2
LIMIT 0;
제약 조건(primary key, check 제약 조건 등)이 있는 테이블을 CTAS 문으로 만들 수는 없어요.
Check 제약 조건
CHECK 제약 조건은 테이블의 모든 행 값이 반드시 충족해야 하는 표현식이에요.
CREATE TABLE t1 (
id INTEGER PRIMARY KEY,
percentage INTEGER CHECK (0 <= percentage AND percentage <= 100)
);
INSERT INTO t1 VALUES (1, 5);
INSERT INTO t1 VALUES (2, -1);
Constraint Error:
CHECK constraint failed: t1
INSERT INTO t1 VALUES (3, 101);
Constraint Error:
CHECK constraint failed: t1
CREATE TABLE t2 (id INTEGER PRIMARY KEY, x INTEGER, y INTEGER CHECK (x < y));
INSERT INTO t2 VALUES (1, 5, 10);
INSERT INTO t2 VALUES (2, 5, 3);
Constraint Error:
CHECK constraint failed: t2
CHECK 제약 조건은 CONSTRAINTS 절의 일부로도 추가할 수 있어요.
CREATE TABLE t3 (
id INTEGER PRIMARY KEY,
x INTEGER,
y INTEGER,
CONSTRAINT x_smaller_than_y CHECK (x < y)
);
INSERT INTO t3 VALUES (1, 5, 10);
INSERT INTO t3 VALUES (2, 5, 3);
Constraint Error:
CHECK constraint failed: t3
외래 키 제약 조건 (Foreign Key Constraints)
FOREIGN KEY는 다른 테이블의 primary key를 참조하는 열(또는 열 집합)이에요. 외래 키는 참조 무결성을 확인해요. 즉 삽입 시 참조된 primary key가 다른 테이블에 존재해야 해요.
CREATE TABLE t1 (id INTEGER PRIMARY KEY, j VARCHAR);
CREATE TABLE t2 (
id INTEGER PRIMARY KEY,
t1_id INTEGER,
FOREIGN KEY (t1_id) REFERENCES t1 (id)
);
예시:
INSERT INTO t1 VALUES (1, 'a');
INSERT INTO t2 VALUES (1, 1);
INSERT INTO t2 VALUES (2, 2);
Constraint Error:
Violates foreign key constraint because key "id: 2" does not exist in the referenced table
외래 키는 복합 primary key에 정의할 수 있어요.
CREATE TABLE t3 (id INTEGER, j VARCHAR, PRIMARY KEY (id, j));
CREATE TABLE t4 (
id INTEGER PRIMARY KEY, t3_id INTEGER, t3_j VARCHAR,
FOREIGN KEY (t3_id, t3_j) REFERENCES t3(id, j)
);
예시:
INSERT INTO t3 VALUES (1, 'a');
INSERT INTO t4 VALUES (1, 1, 'a');
INSERT INTO t4 VALUES (2, 1, 'b');
Constraint Error:
Violates foreign key constraint because key "id: 1, j: b" does not exist in the referenced table
외래 키는 unique 열에도 정의할 수 있어요.
CREATE TABLE t5 (id INTEGER UNIQUE, j VARCHAR);
CREATE TABLE t6 (
id INTEGER PRIMARY KEY,
t5_id INTEGER,
FOREIGN KEY (t5_id) REFERENCES t5(id)
);
Limitation
외래 키에는 다음 제한이 있어요.
연쇄 삭제가 있는 외래 키(FOREIGN KEY ... REFERENCES ... ON DELETE CASCADE)는 지원되지 않아요.
자기 참조 외래 키가 있는 테이블에 삽입하는 것은 현재 지원되지 않으며 다음 오류가 나요.
Constraint Error:
Violates foreign key constraint because key "..." does not exist in the referenced table.
Generated Columns
[type] [GENERATED ALWAYS] AS (expr) [VIRTUAL|STORED] 문법은 generated column을 만들어요. 이 종류의 열의 데이터는 그 표현식에서 생성되는데, 표현식은 테이블의 다른(일반 또는 generated) 열을 참조할 수 있어요. 계산으로 만들어지므로 이런 열에는 직접 삽입할 수 없어요.
DuckDB는 표현식의 반환 타입에 기반해 generated column의 타입을 추론할 수 있어요. 따라서 generated column을 선언할 때 타입을 생략할 수 있어요. 타입을 명시적으로 설정할 수도 있지만, 타입을 generated column의 타입으로 캐스팅할 수 없으면 참조된 열에 삽입이 실패할 수 있어요.
Generated column에는 VIRTUAL과 STORED 두 가지 종류가 있어요. 가상 generated column의 데이터는 디스크에 저장되지 않고, 열이 참조될 때마다(select 문을 통해) 표현식에서 계산돼요.
저장 generated column의 데이터는 디스크에 저장되고, 그 의존 데이터가 변경될 때마다(INSERT / UPDATE / DROP 문을 통해) 계산돼요.
현재는 VIRTUAL 종류만 지원되며, 마지막 필드를 비워 두면 그것도 기본 옵션이에요.
generated column의 가장 간단한 문법:
타입은 표현식에서 파생되고, 변형은 기본적으로 VIRTUAL이에요.
CREATE TABLE t1 (x FLOAT, two_x AS (2 * x));
같은 generated column을 완전하게 지정해 보기:
CREATE TABLE t1 (x FLOAT, two_x FLOAT GENERATED ALWAYS AS (2 * x) VIRTUAL);