CREATE TABLE
CREATE TABLE
이 페이지는 SQLite에서 새 테이블을 생성하는 CREATE TABLE 명령어에 대해 설명합니다. 테이블의 이름, 열(column), 데이터 타입, 제약 조건 등을 정의하는 방법과 사용할 수 있는 다양한 옵션을 다룹니다.
출처: 문서
본문
1. 문법
create-table-stmt:
CREATE TEMP TEMPORARY TABLE IF NOT EXISTS schema-name . table-name ( column-def table-constraint , ) table-options , AS select-stmt
column-def:
column-name type-name column-constraint
column-constraint:
CONSTRAINT name PRIMARY KEY DESC conflict-clause AUTOINCREMENT ASC NOT NULL conflict-clause UNIQUE conflict-clause CHECK ( expr ) DEFAULT ( expr ) literal-value signed-number COLLATE collation-name foreign-key-clause GENERATED ALWAYS AS ( expr ) VIRTUAL STORED
conflict-clause:
ON CONFLICT ROLLBACK ABORT FAIL IGNORE REPLACE
expr:
literal-value bind-parameter schema-name . table-name . column-name unary-operator expr expr binary-operator expr function-name ( function-arguments ) filter-clause over-clause ( expr ) , CAST ( expr AS type-name ) expr COLLATE collation-name expr NOT LIKE GLOB REGEXP MATCH expr expr ESCAPE expr expr ISNULL NOTNULL NOT NULL expr IS NOT DISTINCT FROM expr expr NOT BETWEEN expr AND expr expr NOT IN ( select-stmt ) expr , schema-name . table-function ( expr ) table-name , NOT EXISTS ( select-stmt ) CASE expr WHEN expr THEN expr ELSE expr END raise-function
filter-clause:
FILTER ( WHERE expr )
function-arguments:
DISTINCT expr , * ORDER BY ordering-term ,
ordering-term:
expr COLLATE collation-name DESC ASC NULLS FIRST NULLS LAST
over-clause:
OVER window-name ( base-window-name PARTITION BY expr , ORDER BY ordering-term , frame-spec )
frame-spec:
GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING RANGE ROWS UNBOUNDED PRECEDING expr PRECEDING CURRENT ROW expr PRECEDING CURRENT ROW expr FOLLOWING expr PRECEDING CURRENT ROW expr FOLLOWING EXCLUDE CURRENT ROW EXCLUDE GROUP EXCLUDE TIES EXCLUDE NO OTHERS
ordering-term:
expr COLLATE collation-name DESC ASC NULLS FIRST NULLS LAST
raise-function:
RAISE ( ROLLBACK , expr ) IGNORE ABORT FAIL
foreign-key-clause:
REFERENCES foreign-table ( column-name ) , ON DELETE SET NULL UPDATE SET DEFAULT CASCADE RESTRICT NO ACTION MATCH name NOT DEFERRABLE INITIALLY DEFERRED INITIALLY IMMEDIATE
literal-value:
CURRENT_TIMESTAMP numeric-literal string-literal blob-literal NULL TRUE FALSE CURRENT_TIME CURRENT_DATE
signed-number:
+ numeric-literal -
type-name:
name ( signed-number , signed-number ) ( signed-number )
signed-number:
+ numeric-literal -
select-stmt:
WITH RECURSIVE common-table-expression , SELECT DISTINCT result-column , ALL FROM table-or-subquery join-clause , WHERE expr GROUP BY expr HAVING expr , WINDOW window-name AS window-defn , VALUES ( expr ) , , compound-operator select-core ORDER BY LIMIT expr ordering-term , OFFSET expr , expr
common-table-expression:
table-name ( column-name ) AS NOT MATERIALIZED ( select-stmt ) ,
compound-operator:
UNION UNION INTERSECT EXCEPT ALL
expr:
literal-value bind-parameter schema-name . table-name . column-name unary-operator expr expr binary-operator expr function-name ( function-arguments ) filter-clause over-clause ( expr ) , CAST ( expr AS type-name ) expr COLLATE collation-name expr NOT LIKE GLOB REGEXP MATCH expr expr ESCAPE expr expr ISNULL NOTNULL NOT NULL expr IS NOT DISTINCT FROM expr expr NOT BETWEEN expr AND expr expr NOT IN ( select-stmt ) expr , schema-name . table-function ( expr ) table-name , NOT EXISTS ( select-stmt ) CASE expr WHEN expr THEN expr ELSE expr END raise-function
filter-clause:
FILTER ( WHERE expr )
function-arguments:
DISTINCT expr , * ORDER BY ordering-term ,
literal-value:
CURRENT_TIMESTAMP numeric-literal string-literal blob-literal NULL TRUE FALSE CURRENT_TIME CURRENT_DATE
over-clause:
OVER window-name ( base-window-name PARTITION BY expr , ORDER BY ordering-term , frame-spec )
frame-spec:
GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING RANGE ROWS UNBOUNDED PRECEDING expr PRECEDING CURRENT ROW expr PRECEDING CURRENT ROW expr FOLLOWING expr PRECEDING CURRENT ROW expr FOLLOWING EXCLUDE CURRENT ROW EXCLUDE GROUP EXCLUDE TIES EXCLUDE NO OTHERS
raise-function:
RAISE ( ROLLBACK , expr ) IGNORE ABORT FAIL
type-name:
name ( signed-number , signed-number ) ( signed-number )
signed-number:
+ numeric-literal -
join-clause:
table-or-subquery join-operator table-or-subquery join-constraint
join-constraint:
USING ( column-name ) , ON expr
join-operator:
NATURAL LEFT OUTER JOIN , RIGHT FULL INNER CROSS
ordering-term:
expr COLLATE collation-name DESC ASC NULLS FIRST NULLS LAST
result-column:
expr AS column-alias * table-name . *
table-or-subquery:
schema-name . table-name AS table-alias INDEXED BY index-name NOT INDEXED table-function-name ( expr ) , AS table-alias ( select-stmt ) ( table-or-subquery ) , join-clause
window-defn:
( base-window-name PARTITION BY expr , ORDER BY ordering-term , frame-spec )
frame-spec:
GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING RANGE ROWS UNBOUNDED PRECEDING expr PRECEDING CURRENT ROW expr PRECEDING CURRENT ROW expr FOLLOWING expr PRECEDING CURRENT ROW expr FOLLOWING EXCLUDE CURRENT ROW EXCLUDE GROUP EXCLUDE TIES EXCLUDE NO OTHERS
table-constraint:
CONSTRAINT name PRIMARY KEY ( indexed-column AUTOINCREMENT ) UNIQUE ( indexed-column ) conflict-clause , CHECK ( expr ) FOREIGN KEY ( column-name ) foreign-key-clause ,
conflict-clause:
ON CONFLICT ROLLBACK ABORT FAIL IGNORE REPLACE
expr:
literal-value bind-parameter schema-name . table-name . column-name unary-operator expr expr binary-operator expr function-name ( function-arguments ) filter-clause over-clause ( expr ) , CAST ( expr AS type-name ) expr COLLATE collation-name expr NOT LIKE GLOB REGEXP MATCH expr expr ESCAPE expr expr ISNULL NOTNULL NOT NULL expr IS NOT DISTINCT FROM expr expr NOT BETWEEN expr AND expr expr NOT IN ( select-stmt ) expr , schema-name . table-function ( expr ) table-name , NOT EXISTS ( select-stmt ) CASE expr WHEN expr THEN expr ELSE expr END raise-function
filter-clause:
FILTER ( WHERE expr )
function-arguments:
DISTINCT expr , * ORDER BY ordering-term ,
ordering-term:
expr COLLATE collation-name DESC ASC NULLS FIRST NULLS LAST
literal-value:
CURRENT_TIMESTAMP numeric-literal string-literal blob-literal NULL TRUE FALSE CURRENT_TIME CURRENT_DATE
over-clause:
OVER window-name ( base-window-name PARTITION BY expr , ORDER BY ordering-term , frame-spec )
frame-spec:
GROUPS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING RANGE ROWS UNBOUNDED PRECEDING expr PRECEDING CURRENT ROW expr PRECEDING CURRENT ROW expr FOLLOWING expr PRECEDING CURRENT ROW expr FOLLOWING EXCLUDE CURRENT ROW EXCLUDE GROUP EXCLUDE TIES EXCLUDE NO OTHERS
ordering-term:
expr COLLATE collation-name DESC ASC NULLS FIRST NULLS LAST
raise-function:
RAISE ( ROLLBACK , expr ) IGNORE ABORT FAIL
type-name:
name ( signed-number , signed-number ) ( signed-number )
signed-number:
+ numeric-literal -
foreign-key-clause:
REFERENCES foreign-table ( column-name ) , ON DELETE SET NULL UPDATE SET DEFAULT CASCADE RESTRICT NO ACTION MATCH name NOT DEFERRABLE INITIALLY DEFERRED INITIALLY IMMEDIATE
indexed-column:
column-name COLLATE collation-name DESC expr ASC
table-options:
WITHOUT ROWID STRICT ,
2. CREATE TABLE 명령
"CREATE TABLE" 명령은 SQLite 데이터베이스에 새 테이블을 만드는 데 사용해요. CREATE TABLE 명령은 새 테이블의 다음 속성들을 지정해요.
- 새 테이블의 이름.
- 새 테이블이 생성될 데이터베이스. 테이블은 main 데이터베이스, temp 데이터베이스, 또는 부착된(attached) 데이터베이스에 만들 수 있어요.
- 테이블에 있는 각 열(column)의 이름.
- 테이블에 있는 각 열의 선언된 타입(declared type).
- 테이블에 있는 각 열의 기본값(default value) 또는 표현식(expression).
- 각 열에 사용할 기본 정렬 순서(collation sequence).
- 테이블의 선택적 PRIMARY KEY. 단일 열 및 복합(여러 열) PRIMARY KEY를 모두 지원해요.
- 테이블 콘텐츠에 대한 0개 이상의 제약 조건. SQLite는 UNIQUE, NOT NULL, CHECK 및 FOREIGN KEY 제약 조건을 지원해요.
- 선택적으로, 생성된 열(generated column) 제약 조건.
- 테이블이 WITHOUT ROWID 테이블인지 여부.
- 테이블이 엄격한 타입 검사(strict type checking)의 적용을 받는지 여부.
모든 CREATE TABLE 문은 새 테이블의 이름을 지정해야 해요. "sqlite_"로 시작하는 테이블 이름은 내부 사용을 위해 예약되어 있어요. "sqlite_"로 시작하는 이름으로 테이블을 만들려고 하면 오류가 발생해요.
schema-name을 지정하는 경우, "main" 또는 "temp"이거나 부착된(attached) 데이터베이스의 이름이어야 해요. 이 경우 새 테이블은 해당 이름의 데이터베이스에 생성돼요. "CREATE"와 "TABLE" 사이에 "TEMP" 또는 "TEMPORARY" 키워드가 있으면 새 테이블은 temp 데이터베이스에 생성돼요. schema-name과 TEMP 또는 TEMPORARY 키워드를 둘 다 지정하면 오류예요. 단, schema-name이 "temp"인 경우는 예외예요. 스키마 이름을 지정하지 않고 TEMP 키워드도 없으면 테이블은 main 데이터베이스에 생성돼요.
같은 이름의 테이블, 인덱스 또는 뷰가 이미 있는 데이터베이스에 새 테이블을 만들려고 하면 대개 오류가 발생해요. 하지만 CREATE TABLE 문의 일부로 "IF NOT EXISTS" 절을 지정했고 같은 이름의 테이블이나 뷰가 이미 존재하면, CREATE TABLE 명령은 아무 효과도 없어요(오류 메시지도 반환되지 않아요). "IF NOT EXISTS" 절을 지정했더라도 기존 인덱스 때문에 테이블을 만들 수 없으면 여전히 오류가 반환돼요.
기존 트리거와 같은 이름의 테이블을 만드는 것은 오류가 아니에요.
테이블은 DROP TABLE 문을 사용하여 제거해요.
2.1. CREATE TABLE ... AS SELECT 문
"CREATE TABLE ... AS SELECT" 문은 SELECT 문의 결과를 기반으로 데이터베이스 테이블을 생성하고 채웁니다. 이 테이블은 SELECT 문이 반환하는 열 수와 동일한 수의 열을 가집니다. 각 열의 이름은 SELECT 문의 결과 집합에 있는 해당 열의 이름과 같습니다. 각 열의 선언 타입은 SELECT 문 결과 집합에 있는 해당 표현식의 표현식 선호도(expression affinity)에 따라 다음과 같이 결정됩니다:
| 표현식 선호도 | 열 선언 타입 |
|---|---|
| TEXT | "TEXT" |
| NUMERIC | "NUM" |
| INTEGER | "INT" |
| REAL | "REAL" |
| BLOB (일명 "NONE") | "" (빈 문자열) |
CREATE TABLE AS를 사용하여 생성된 테이블에는 PRIMARY KEY도 어떤 종류의 제약 조건도 없습니다. 각 열의 기본값은 NULL입니다. 새 테이블의 각 열에 대한 기본 데이터 정렬 순서는 BINARY입니다.
CREATE TABLE AS로 생성된 테이블은 처음에 SELECT 문이 반환한 데이터 행들로 채워집니다. 행에는 SELECT 문이 반환한 순서대로 1부터 시작하여 연속적으로 증가하는 rowid 값이 할당됩니다.
3. 열 정의
CREATE TABLE ... AS SELECT 문이 아닌 경우, CREATE TABLE에는 하나 이상의 열 정의가 포함되며, 선택적으로 테이블 제약 조건 목록이 뒤따를 수 있습니다. 각 열 정의는 열 이름, 선택적으로 열의 선언 타입, 그 다음 하나 이상의 선택적 열 제약 조건으로 구성됩니다. 앞 문장의 목적상 "열 제약 조건"의 정의에는 COLLATE 및 DEFAULT 절도 포함됩니다. 이들은 테이블이 포함할 수 있는 데이터를 제한하지 않는다는 의미에서 실제 제약 조건은 아니지만 말입니다. 나머지 제약 조건(NOT NULL, CHECK, UNIQUE, PRIMARY KEY 및 FOREIGN KEY 제약 조건)은 테이블 데이터에 제한을 가합니다.
테이블의 열 수는 컴파일 타임 매개변수 SQLITE_MAX_COLUMN에 의해 제한됩니다. 테이블의 단일 행은 SQLITE_MAX_LENGTH 바이트를 초과하는 데이터를 저장할 수 없습니다. 이 두 한계는 모두 sqlite3_limit() C/C++ 인터페이스를 사용하여 런타임에 낮출 수 있습니다.
3.1. 열 데이터 타입
대부분의 SQL 데이터베이스와 달리 SQLite는 열의 선언 타입에 따라 해당 열에 삽입될 수 있는 데이터 타입을 제한하지 않습니다. 대신 SQLite는 동적 타이핑(dynamic typing)을 사용합니다. 열의 선언 타입은 오직 열의 선호도(affinity)를 결정하는 데만 사용됩니다.
3.2. DEFAULT 절
DEFAULT 절은 INSERT를 수행할 때 사용자가 명시적으로 값을 제공하지 않으면 열에 사용할 기본값을 지정합니다. 열 정의에 명시적인 DEFAULT 절이 없으면 열의 기본값은 NULL입니다. 명시적인 DEFAULT 절은 기본값이 NULL, 문자열 상수, blob 상수, 부호 있는 숫자(signed-number) 또는 괄호로 묶인 상수 표현식이 되도록 지정할 수 있습니다. 기본값은 특수한 대소문자 구분 없는 키워드인 CURRENT_TIME, CURRENT_DATE 또는 CURRENT_TIMESTAMP 중 하나일 수도 있습니다. DEFAULT 절의 목적상 표현식에 하위 질의, 열 또는 테이블 참조, 바인드 매개변수, 또는 작은따옴표 대신 큰따옴표로 묶인 문자열 리터럴이 포함되어 있지 않으면 상수로 간주됩니다.
INSERT 문이 모든 테이블 열에 대해 명시적인 값을 제공하지 않고 테이블에 새 행을 삽입할 때마다 새 행에 저장되는 값은 다음과 같이 기본값에 의해 결정됩니다:
-
열의 기본값이 상수 NULL, 텍스트, blob 또는 부호 있는 숫자 값이면 해당 값이 새 행에 직접 사용됩니다.
-
열의 기본값이 괄호 안의 표현식이면 표현식은 삽입되는 각 행에 대해 한 번 평가되며 그 결과가 새 행에 사용됩니다.
-
열의 기본값이 CURRENT_TIME, CURRENT_DATE 또는 CURRENT_TIMESTAMP이면 새 행에 사용되는 값은 현재 UTC 날짜 및/또는 시간의 텍스트 표현입니다. CURRENT_TIME의 경우 값의 형식은 "HH:MM:SS"입니다. CURRENT_DATE의 경우 "YYYY-MM-DD"입니다. CURRENT_TIMESTAMP의 형식은 "YYYY-MM-DD HH:MM:SS"입니다.
3.3. COLLATE 절
COLLATE 절은 열의 기본 데이터 정렬 순서로 사용할 데이터 정렬 순서(collating sequence)의 이름을 지정합니다. COLLATE 절이 지정되지 않은 경우 기본 데이터 정렬 순서는 BINARY입니다.
3.4. GENERATED ALWAYS AS 절
GENERATED ALWAYS AS 절을 포함하는 열은 생성 열(generated column)입니다. 생성 열은 SQLite 버전 3.31.0(2020-01-22)부터 지원됩니다. 생성 열의 기능과 제한 사항에 대한 자세한 내용은 별도 문서를 참조하세요.
3.5. PRIMARY KEY
SQLite의 각 테이블은 최대 하나의 PRIMARY KEY를 가질 수 있습니다. 열 정의에 PRIMARY KEY 키워드가 추가되면 테이블의 기본 키는 해당 단일 열로 구성됩니다. 또는 PRIMARY KEY 절이 테이블 제약 조건으로 지정되면 테이블의 기본 키는 PRIMARY KEY 절의 일부로 지정된 열 목록으로 구성됩니다. PRIMARY KEY 절에는 열 이름만 포함되어야 합니다. PRIMARY KEY의 인덱스 열에 표현식을 사용하는 것은 지원되지 않습니다. CREATE TABLE 문에 PRIMARY KEY 절이 두 개 이상 나타나면 오류가 발생합니다. PRIMARY KEY는 일반 테이블에서는 선택 사항이지만 WITHOUT ROWID 테이블에서는 필수입니다.
테이블에 단일 열 기본 키가 있고 해당 열의 선언 타입이 "INTEGER"이며 테이블이 WITHOUT ROWID 테이블이 아닌 경우, 해당 열을 INTEGER PRIMARY KEY라고 합니다. INTEGER PRIMARY KEY와 관련된 특별한 속성 및 동작에 대한 설명은 아래를 참조하세요.
기본 키가 있는 테이블의 각 행은 기본 키 열에 고유한 값 조합을 가져야 합니다. 기본 키 값의 고유성을 결정할 때 NULL 값은 다른 NULL을 포함한 모든 다른 값과 구별되는 것으로 간주됩니다. INSERT 또는 UPDATE 문이 두 개 이상의 행이 동일한 기본 키 값을 갖도록 테이블 내용을 수정하려고 하면 제약 조건 위반입니다.
SQL 표준에 따르면 PRIMARY KEY는 항상 NOT NULL을 의미해야 합니다. 불행히도 일부 초기 버전의 버그로 인해 SQLite에서는 그렇지 않습니다. 열이 INTEGER PRIMARY KEY이거나 테이블이 WITHOUT ROWID 테이블 또는 STRICT 테이블이거나 열이 NOT NULL로 선언된 경우를 제외하고, SQLite는 PRIMARY KEY 열에 NULL 값을 허용합니다. SQLite가 표준을 따르도록 수정될 수 있지만 그렇게 하면 레거시 애플리케이션이 손상될 수 있습니다. 따라서 SQLite가 대부분의 PRIMARY KEY 열에서 NULL을 허용한다는 사실을 단지 문서화하기로 결정되었습니다.
3.6. UNIQUE 제약 조건
UNIQUE 제약 조건은 PRIMARY KEY 제약 조건과 유사하지만, 하나의 테이블에 여러 개의 UNIQUE 제약 조건을 가질 수 있다는 점이 다릅니다. 테이블의 각 UNIQUE 제약 조건에 대해 각 행은 UNIQUE 제약 조건으로 식별되는 열들에 고유한 값 조합을 가져야 합니다. UNIQUE 제약 조건의 목적상 NULL 값은 다른 NULL을 포함한 모든 다른 값과 구별되는 것으로 간주됩니다. PRIMARY KEY와 마찬가지로 UNIQUE 테이블 제약 조건 절에는 열 이름만 포함되어야 합니다. UNIQUE 테이블 제약 조건의 인덱스 열에 표현식을 사용하는 것은 지원되지 않습니다.
대부분의 경우 UNIQUE 및 PRIMARY KEY 제약 조건은 데이터베이스에 고유 인덱스(unique index)를 생성하여 구현됩니다. (예외는 INTEGER PRIMARY KEY와 WITHOUT ROWID 테이블의 PRIMARY KEY입니다.) 따라서 다음 스키마는 논리적으로 동일합니다:
CREATE TABLE t1(a, b UNIQUE);CREATE TABLE t1(a, b PRIMARY KEY);CREATE TABLE t1(a, b);CREATE UNIQUE INDEX t1b ON t1(b);
3.7. CHECK 제약 조건
CHECK 제약 조건은 열 정의에 첨부하거나 테이블 제약 조건으로 지정할 수 있습니다. 실질적으로는 차이가 없습니다. 새 행이 테이블에 삽입되거나 기존 행이 업데이트될 때마다 각 CHECK 제약 조건과 연관된 표현식이 평가되고 CAST 표현식과 동일한 방식으로 NUMERIC 값으로 캐스팅됩니다. 결과가 0(정수 값 0 또는 실수 값 0.0)이면 제약 조건 위반이 발생한 것입니다. CHECK 표현식이 NULL 또는 다른 0이 아닌 값으로 평가되면 제약 조건 위반이 아닙니다. CHECK 제약 조건의 표현식에는 하위 질의가 포함될 수 없습니다.
CHECK 제약 조건은 테이블이 기록될 때만 검증되며 읽을 때는 검증되지 않습니다. 또한 "PRAGMA ignore_check_constraints=ON;" 문을 사용하여 CHECK 제약 조건 검증을 일시적으로 비활성화할 수 있습니다. 따라서 쿼리가 CHECK 제약 조건을 위반하는 결과를 생성할 가능성이 있습니다.
3.8. NOT NULL 제약 조건
NOT NULL 제약 조건은 열 정의에만 첨부할 수 있으며 테이블 제약 조건으로 지정할 수 없습니다. 당연하게도 NOT NULL 제약 조건은 관련 열이 NULL 값을 포함할 수 없음을 규정합니다. 새 행을 삽입하거나 기존 행을 업데이트할 때 열 값을 NULL로 설정하려고 하면 제약 조건 위반이 발생합니다. NOT NULL 제약 조건은 쿼리 중에는 검증되지 않으므로, 데이터베이스 파일이 손상된 경우 열이 NOT NULL로 표시되어 있어도 해당 열에 대한 쿼리가 NULL 값을 생성할 수 있습니다.
4. 제약 조건 시행
제약 조건은 INSERT와 UPDATE 중에 검사되고, PRAGMA integrity_check와 PRAGMA quick_check, 그리고 때때로 ALTER TABLE에 의해서도 검사돼요. 일반적으로 쿼리와 DELETE 문은 제약 조건을 확인하지 않아요. 따라서 데이터베이스 파일이 손상된 경우(예: 외부 프로그램이 SQLite 라이브러리를 거치지 않고 데이터베이스 파일을 직접 수정한 경우) 쿼리가 제약 조건을 위반하는 데이터를 반환할 수 있어요. 예를 들어:
CREATE TABLE t1(x INT CHECK( x>3 ));
/* Insert a row with X less than 3 by directly writing into the
** database file using an external program */
PRAGMA integrity_check; -- Reports row with x less than 3 as corrupt
INSERT INTO t1(x) VALUES(2); -- Fails with SQLITE_CORRUPT
SELECT x FROM t1; -- Returns an integer less than 3 in spite of the CHECK constraint
CHECK 제약 조건의 시행은 PRAGMA ignore_check_constraints=ON; 문을 사용하여 일시적으로 비활성화할 수 있어요.
4.1. 제약 조건 위반에 대한 대응
제약 조건 위반에 대한 대응은 제약 조건 충돌 해결 알고리즘에 의해 결정돼요. 각 PRIMARY KEY, UNIQUE, NOT NULL 및 CHECK 제약 조건에는 기본 충돌 해결 알고리즘이 있어요. PRIMARY KEY, UNIQUE 및 NOT NULL 제약 조건은 정의에 conflict-clause를 포함하여 다른 기본 충돌 해결 알고리즘을 명시적으로 지정할 수 있어요. 또는 제약 조건 정의에 conflict-clause가 포함되지 않은 경우 기본 충돌 해결 알고리즘은 ABORT예요. CHECK 제약 조건의 충돌 해결 알고리즘은 항상 ABORT예요. (역사적 호환성을 위해서만, 테이블 CHECK 제약 조건에는 충돌 해결 절을 허용하지만 효과는 없어요.) 같은 테이블 안의 서로 다른 제약 조건은 서로 다른 기본 충돌 해결 알고리즘을 가질 수 있어요. 추가 정보는 ON CONFLICT 섹션을 참조하세요.
5. ROWID와 INTEGER PRIMARY KEY
WITHOUT ROWID 테이블을 제외하고, SQLite 테이블의 모든 행은 해당 테이블 내에서 그 행을 고유하게 식별하는 64비트 부호 있는 정수 키를 가져요. 이 정수를 보통 "rowid"라고 불러요. rowid 값은 열 이름 대신 대소문자를 구분하지 않는 특별한 이름인 "rowid", "oid", "rowid" 중 하나를 사용하여 접근할 수 있어요. 테이블에 사용자 정의 열 이름으로 "rowid", "oid" 또는 "rowid"가 있다면, 그 이름은 항상 명시적으로 선언된 열을 가리키며 정수 rowid 값을 검색하는 데 사용할 수 없어요.
rowid(및 "oid", "rowid")는 WITHOUT ROWID 테이블에서는 생략돼요. WITHOUT ROWID 테이블은 SQLite 버전 3.8.2(2013-12-06) 이상에서만 사용할 수 있어요. WITHOUT ROWID 절이 없는 테이블을 "rowid 테이블"이라고 불러요.
rowid 테이블의 데이터는 각 테이블 행에 대해 하나의 항목을 포함하는 B-Tree 구조로 저장되며, rowid 값을 키로 사용해요. 즉, rowid로 레코드를 검색하거나 정렬하는 것이 빨라요. 특정 rowid를 가진 레코드를 검색하거나 지정된 범위 내의 rowid를 가진 모든 레코드를 검색하는 것은 다른 PRIMARY KEY나 인덱스된 값을 지정하여 수행하는 유사한 검색보다 약 2배 빨라요.
아래에 언급되는 한 가지 예외를 제외하면, rowid 테이블의 기본 키가 단일 열로 구성되고 그 열의 선언된 타입이 대소문자를 구분하지 않고 "INTEGER"인 경우, 그 열은 rowid의 별칭이 돼요. 이러한 열을 보통 "integer primary key"라고 불러요. PRIMARY KEY 열은 선언된 타입 이름이 정확히 "INTEGER"인 경우에만 integer primary key가 돼요. "INT"나 "BIGINT", "SHORT INTEGER", "UNSIGNED INTEGER" 같은 다른 정수 타입 이름은 기본 키 열을 rowid의 별칭이 아닌, integer affinity와 고유 인덱스를 가진 일반 테이블 열로 동작하게 해요.
위에서 언급한 예외는 선언된 타입이 "INTEGER"인 열의 선언에 "PRIMARY KEY DESC" 절이 포함된 경우, 그 열은 rowid의 별칭이 되지 않고 integer primary key로 분류되지 않아요. 이 특이한 동작은 의도된 것이 아니에요. 초기 SQLite 버전의 버그 때문이에요. 하지만 버그를 고치면 하위 호환성이 깨질 수 있어요. 따라서 호환성 파괴보다는 극히 드문 경우의 이상한 동작이 훨씬 낫기 때문에 원래 동작이 유지(및 문서화)되었어요. 즉, 다음 세 가지 테이블 선언은 모두 "x" 열을 rowid의 별칭(integer primary key)으로 만들어요:
-
CREATE TABLE t(x INTEGER PRIMARY KEY ASC, y, z); -
CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x ASC)); -
CREATE TABLE t(x INTEGER, y, z, PRIMARY KEY(x DESC));
그러나 다음 선언은 "x"가 rowid의 별칭이 되지 않아요:
CREATE TABLE t(x INTEGER PRIMARY KEY DESC, y, z);
rowid 값은 다른 열 값과 동일한 방식으로 UPDATE 문을 사용하여 수정할 수 있어요. 내장된 별칭("rowid", "oid" 또는 "rowid")이나 integer primary key로 생성된 별칭을 사용하면 돼요. 마찬가지로 INSERT 문은 삽입되는 각 행의 rowid로 사용할 값을 제공할 수 있어요. 일반 SQLite 열과 달리 integer primary key 또는 rowid 열은 정수 값만 포함해야 해요. integer primary key 또는 rowid 열은 부동 소수점 값, 문자열, BLOB, NULL을 보유할 수 없어요.
UPDATE 문이 integer primary key 또는 rowid 열을 NULL이나 blob 값으로 설정하려 하거나, 정수로 손실 없이 변환할 수 없는 문자열이나 실수 값으로 설정하려 하면 "datatype mismatch" 오류가 발생하고 문이 중단돼요. INSERT 문이 integer primary key 또는 rowid 열에 blob 값, 또는 정수로 손실 없이 변환할 수 없는 문자열이나 실수 값을 삽입하려 하면 "datatype mismatch" 오류가 발생하고 문이 중단돼요.
INSERT 문이 rowid 또는 integer primary key 열에 NULL 값을 삽입하려 하면, 시스템이 rowid로 사용할 정수 값을 자동으로 선택해요. 이 작업이 수행되는 방법에 대한 자세한 설명은 별도로 제공돼요.
외래 키 제약 조건의 부모 키는 rowid를 사용할 수 없어요. 부모 키는 명명된 열만 사용해야 해요.
이 페이지는 2025-04-30 20:02:34Z에 마지막으로 업데이트되었어요.