CREATE TABLE
CREATE TABLE은 컬럼 정의, 데이터 타입, 제약을 갖춘 새 테이블을 데이터베이스에 만들어요.
출처: 문서
본문
컬럼 정의, 데이터 타입, 제약을 담은 새 테이블을 데이터베이스에 만들어요.
문법
CREATE TABLE [IF NOT EXISTS] [schema-name.]table-name (
column-def [, column-def ...] [, table-constraint [, table-constraint ...]]
) [STRICT];
컬럼 정의
column-name [type-name] [column-constraint ...]
컬럼 제약
PRIMARY KEY [ASC | DESC] [AUTOINCREMENT]
NOT NULL
UNIQUE
DEFAULT expression
CHECK (expression)
REFERENCES foreign-table [(column-name)] [foreign-key-actions]
COLLATE collation-name
테이블 제약
PRIMARY KEY (column-name [, column-name ...])
UNIQUE (column-name [, column-name ...])
CHECK (expression)
FOREIGN KEY (column-name [, column-name ...]) REFERENCES foreign-table [(column-name [, column-name ...])] [foreign-key-actions]
설명
CREATE TABLE은 데이터베이스에 새 테이블을 정의해요. 테이블 이름은 데이터베이스 스키마 안에서 고유해야 해요. 각 테이블은 하나 이상의 컬럼 정의와 선택적인 테이블 수준 제약으로 이루어져요.
매개변수
| 매개변수 | 설명 |
|---|---|
IF NOT EXISTS |
같은 이름의 테이블이 이미 있을 때 오류를 막아줘요. 테이블이 있으면 문은 아무 것도 하지 않아요. |
schema-name |
붙은(attached) 데이터베이스 이름. 생략하면 main 데이터베이스 |
table-name |
만들 테이블 이름 |
STRICT |
테이블에서 엄격한 타입 검사를 켜요. STRICT 테이블을 참고하세요. |
컬럼 정의
각 컬럼 정의는 컬럼 이름, 선택적 타입, 0개 이상의 컬럼 제약으로 이루어져요.
타입을 지정하지 않으면 그 컬럼은 어떤 저장 클래스도 받아들여요. 타입 이름을 지정하면 그 이름이 컬럼의 타입 어피니티를 결정하고, 삽입된 값이 어떻게 저장되는지에 영향을 줘요.
CREATE TABLE measurements (
id INTEGER PRIMARY KEY,
sensor_name TEXT NOT NULL,
reading REAL,
raw_data BLOB,
notes -- no type: accepts any storage class
);
컬럼 제약
PRIMARY KEY
컬럼을 테이블의 기본 키로 지정해요. 테이블은 기본 키를 최대 하나만 가질 수 있어요.
CREATE TABLE users (
id INTEGER PRIMARY KEY
);
컬럼이 INTEGER PRIMARY KEY로 선언되면 내부 rowid의 별칭이 되고, 삽입된 정수 값이 곧 행 식별자로 쓰여요. 이게 가장 효율적인 기본 키 형태예요.
AUTOINCREMENT
AUTOINCREMENT 키워드는 INTEGER PRIMARY KEY와만 쓸 수 있어요. sqlite_sequence 시스템 테이블에 카운터를 유지해서, 이전에 삭제된 행의 rowid 값을 재사용하지 못하게 막아줘요.
CREATE TABLE events (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT
);
AUTOINCREMENT가 없으면 Turso는 행이 삭제된 후 rowid 값을 재사용할 수 있어요. AUTOINCREMENT가 있으면 자동 생성된 rowid가, 행이 삭제됐더라도, 그 테이블의 커밋된 행에 존재했던 어떤 rowid보다 항상 커요.
SELECT MAX(value) FROM "__turso_internal_seq___turso_internal_autoincrement_<table_name>";
이 행은 커밋된(그리고 영구 소실된) 모든 nextval에 의해 즉시 갱신돼요. PRAGMA wal_checkpoint(TRUNCATE) 이후에는 sqlite_sequence가 AUTOINCREMENT 테이블마다 한 행으로 합쳐지고, 그때부터는 평범한 SQLite 호환 쿼리로 안전하게 읽을 수 있어요.
또한 MVCC autoincrement는 롤백된 rowid를 되돌리지 않아요 — INSERT 이후의 ROLLBACK은 rowid를 재활용하는 대신 갭을 남겨요. SQLite WAL 모드와 Turso의 WAL 모드는 둘 다 롤백 시 되돌리는데, MVCC 모드만 소실을 유지해요.
NOT NULL
컬럼에 NULL 값이 들어가지 못하게 막아요. 컬럼을 NULL로 만들려는 INSERT나 UPDATE는 제약 오류를 일으켜요.
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price REAL NOT NULL
);
UNIQUE
컬럼의 모든 값이 서로 다르게 보장해요. NULL 값은 서로 다른 것으로 취급돼요 (UNIQUE 컬럼에는 NULL이 여러 개 들어갈 수 있어요).
CREATE TABLE accounts (
id INTEGER PRIMARY KEY,
email TEXT UNIQUE
);
DEFAULT
INSERT 문이 값을 주지 않았을 때 컬럼에 쓰일 기본값을 지정해요.
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
status TEXT DEFAULT 'pending',
created_at TEXT DEFAULT CURRENT_TIMESTAMP,
quantity INTEGER DEFAULT 1
);
기본 표현식은 이런 것들이 될 수 있어요:
| 표현식 | 예제 |
|---|---|
| 리터럴 값 | DEFAULT 42, DEFAULT 'active' |
| NULL | DEFAULT NULL |
CURRENT_TIME |
HH:MM:SS 형식의 현재 시각 |
CURRENT_DATE |
YYYY-MM-DD 형식의 현재 날짜 |
CURRENT_TIMESTAMP |
YYYY-MM-DD HH:MM:SS 형식의 현재 날짜·시각 |
| 괄호로 묶인 표현식 | DEFAULT (1 + 1) |
CHECK
테이블의 모든 행에 대해 참(또는 NULL)으로 평가되어야 하는 불리언 표현식을 정의해요. 표현식은 같은 행의 아무 컬럼이나 참조할 수 있어요.
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
age INTEGER CHECK (age >= 18),
salary REAL CHECK (salary > 0)
);
REFERENCES (외래 키)
컬럼과 다른 테이블의 컬럼 사이에 외래 키 관계를 맺어줘요. 전체 문법은 외래 키 제약을 참고하세요.
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER REFERENCES customers(id)
);
COLLATE
컬럼에서 텍스트 비교와 정렬에 쓰일 콜레이션 시퀀스를 지정해요.
| 콜레이션 | 설명 |
|---|---|
BINARY |
원시 바이트 값으로 텍스트를 비교해요 (기본값) |
NOCASE |
ASCII 문자에 대한 대소문자 무시 비교 |
RTRIM |
BINARY와 같지만 뒤쪽 공백은 무시해요 |
Turso는 로캘 인식 콜레이션과 연결에 등록된 커스텀 콜레이션도 해석해요. 알 수 없는 콜레이션 이름은 조용히 무시되는 대신 이제 no such collation sequence 오류를 일으켜요.
CREATE TABLE contacts (
id INTEGER PRIMARY KEY,
name TEXT COLLATE NOCASE,
code TEXT COLLATE RTRIM
);
테이블 제약
테이블 제약은 하나 이상의 컬럼에 적용되고, 모든 컬럼 정의 뒤에 지정해요.
복합 PRIMARY KEY
여러 컬럼에 걸친 기본 키를 정의해요. 키 컬럼들의 값 조합은 각각 고유해야 해요.
CREATE TABLE enrollment (
student_id INTEGER,
course_id INTEGER,
semester TEXT,
grade REAL,
PRIMARY KEY (student_id, course_id, semester)
);
복합 UNIQUE
지정한 컬럼들에 걸친 값 조합이 각각 고유하도록 보장해요.
CREATE TABLE assignments (
employee_id INTEGER,
project_id INTEGER,
role TEXT,
UNIQUE (employee_id, project_id)
);
테이블 수준 CHECK
테이블 수준의 CHECK 제약은 여러 컬럼을 참조할 수 있어요.
CREATE TABLE reservations (
id INTEGER PRIMARY KEY,
start_date TEXT NOT NULL,
end_date TEXT NOT NULL,
CHECK (end_date > start_date)
);
외래 키 제약
외래 키 제약은 테이블 사이의 참조 무결성을 강제해요. 자식 테이블의 값이 부모 테이블의 기존 값과 대응되도록 보장해요.
외래 키 강제는 기본으로 꺼져 있어요. 검사되어야 할 연산을 수행하기 전에 `PRAGMA foreign_keys = ON;`으로 켜세요.문법
-- Column constraint form (single column)
column-name type REFERENCES parent-table(parent-column)
[ON DELETE action] [ON UPDATE action]
[DEFERRABLE INITIALLY DEFERRED]
-- Table constraint form (single or multiple columns)
FOREIGN KEY (column-name [, column-name ...])
REFERENCES parent-table(parent-column [, parent-column ...])
[ON DELETE action] [ON UPDATE action]
[DEFERRABLE INITIALLY DEFERRED]
외래 키 동작
ON DELETE와 ON UPDATE 절은 참조된 부모 행이 삭제되거나 갱신될 때 자식 행에 무슨 일이 일어날지 지정해요.
| 동작 | 설명 |
|---|---|
NO ACTION |
외래 키 제약이 위반되면 오류를 일으켜요 (기본값) |
RESTRICT |
NO ACTION과 같지만, 문장 끝이 아니라 즉시 검사돼요 |
CASCADE |
부모 행을 참조하는 모든 자식 행을 삭제하거나 갱신해요 |
SET NULL |
자식 행의 외래 키 컬럼을 NULL로 설정해요 |
SET DEFAULT |
자식 행의 외래 키 컬럼을 기본값으로 설정해요 |
CREATE TABLE departments (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL
);
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
dept_id INTEGER,
manager_id INTEGER,
FOREIGN KEY (dept_id) REFERENCES departments(id) ON DELETE CASCADE,
FOREIGN KEY (manager_id) REFERENCES employees(id) ON DELETE SET NULL
);
STRICT 테이블
STRICT 테이블은 저장 계층에서 타입 검사를 강제해요. STRICT 컬럼에 삽입되는 모든 값은 선언된 컬럼 타입과 일치하거나, 그 타입으로 손실 없이 변환 가능해야 해요.
CREATE TABLE sensor_data (
id INTEGER PRIMARY KEY,
reading REAL NOT NULL,
label TEXT,
payload BLOB
) STRICT;
허용되는 컬럼 타입
STRICT 테이블은 이 기본 컬럼 타입만 허용해요:
| 타입 | 설명 |
|---|---|
INTEGER |
부호 있는 정수 |
REAL |
부동소수점 수 |
TEXT |
UTF-8 문자열 |
BLOB |
원시 이진 데이터 |
ANY |
모든 저장 클래스 (이 컬럼의 타입 검사를 끔) |
STRICT 테이블의 모든 컬럼은 명시적인 타입 선언을 가져야 해요.
-- This fails: STRICT tables require explicit types on every column
CREATE TABLE bad (
id INTEGER PRIMARY KEY,
data -- error: missing type
) STRICT;
**Turso 확장 기능**: STRICT 테이블은 [CREATE TYPE](/sql-reference/statements/create-type)으로 정의한 커스텀 타입과 [배열 타입](/sql-reference/data-types#array-types)도 지원해요. 커스텀 타입은 사용자 정의 인코딩, 디코딩, 검증, 연산자 오버로딩으로 타입 시스템을 확장해요. 배열 컬럼은 기본 타입에 `[]`를 붙여서 선언해요.
CREATE TABLE events (
id uuid PRIMARY KEY,
name varchar(100) NOT NULL,
event_date date,
is_active boolean DEFAULT 1,
tags TEXT[]
) STRICT;
지원되지 않는 기능
다음 CREATE TABLE 기능은 아직 지원되지 않아요:
| 기능 | 비고 |
|---|---|
CREATE TEMPORARY TABLE |
임시 테이블은 지원되지 않아요 |
CREATE TABLE ... AS SELECT |
쿼리 결과로 테이블을 만드는 건 지원되지 않아요 |
컬럼 제약의 ON CONFLICT 절 |
컬럼 수준 ON CONFLICT 절(예: NOT NULL ON CONFLICT REPLACE)은 지원되지 않아요. 대신 INSERT ... ON CONFLICT 문법을 쓰세요 |
실험적 기능
다음은 실험적 기능 플래그 뒤에서 사용할 수 있어요:
| 기능 | 플래그 | 비고 |
|---|---|---|
WITHOUT ROWID |
--experimental-without-rowid |
WITHOUT ROWID 테이블에는 제한이 있어요; 실험적 기능 페이지를 참고하세요 |
GENERATED ALWAYS AS |
--experimental-generated-columns |
가상 생성(계산) 컬럼만; 저장 컬럼은 아직 지원되지 않아요 |
예제
제약이 있는 기본 테이블
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL UNIQUE,
email TEXT NOT NULL UNIQUE,
display_name TEXT,
bio TEXT DEFAULT '',
created_at TEXT DEFAULT CURRENT_TIMESTAMP
);
외래 키와 CHECK 제약이 있는 테이블
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
total_amount REAL NOT NULL CHECK (total_amount >= 0),
status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending', 'shipped', 'delivered', 'cancelled')),
order_date TEXT NOT NULL DEFAULT CURRENT_DATE,
FOREIGN KEY (customer_id) REFERENCES customers(id) ON DELETE RESTRICT
);
STRICT 테이블
CREATE TABLE inventory (
product_id INTEGER PRIMARY KEY,
sku TEXT NOT NULL UNIQUE,
quantity INTEGER NOT NULL DEFAULT 0 CHECK (quantity >= 0),
price REAL NOT NULL CHECK (price > 0),
category TEXT NOT NULL
) STRICT;
-- This succeeds: '42' is losslessly convertible to INTEGER
INSERT INTO inventory VALUES (1, 'WIDGET-001', '42', 9.99, 'widgets');
-- This fails: 'abc' cannot be converted to INTEGER
INSERT INTO inventory VALUES (2, 'GADGET-001', 'abc', 19.99, 'gadgets');
-- Error: cannot store TEXT value in INTEGER column
외래 키가 있는 복합 기본 키
CREATE TABLE course_enrollment (
student_id INTEGER NOT NULL,
course_id INTEGER NOT NULL,
enrolled_date TEXT DEFAULT CURRENT_DATE,
grade REAL CHECK (grade >= 0.0 AND grade <= 4.0),
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
FOREIGN KEY (course_id) REFERENCES courses(id) ON DELETE CASCADE
);
IF NOT EXISTS
-- Safe to run multiple times
CREATE TABLE IF NOT EXISTS sessions (
id INTEGER PRIMARY KEY,
user_id INTEGER NOT NULL,
token TEXT NOT NULL UNIQUE,
expires_at TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
더 알아보기 (Learn more)
- ALTER TABLE - 기존 테이블 수정하기
- DROP TABLE - 테이블 제거하기
- 데이터 타입 - 타입 어피니티와 STRICT 테이블 타입
- CREATE INDEX - 테이블 컬럼 인덱싱
- CREATE TYPE - STRICT 테이블의 커스텀 타입
- INSERT - 테이블에 행 추가하기