CREATE TABLE
CREATE TABLE
테이블을 만드는 일은 어떤 데이터베이스 작업이든 가장 기본이 되는 시작이에요. CREATE TABLE은 현재 데이터베이스에 새롭고 처음엔 비어 있는 테이블을 만들고, 그 소유자는 명령을 실행한 사용자가 돼요. 테이블 하나를 만들 때 스키마 위치, 임시 여부, 컬럼 타입, 각종 제약 조건까지 한 번에 정리해 주는 명령이죠.
CREATE TABLE은 컬럼 정의와 테이블 제약 조건을 비롯해, 필요하면 파티셔닝·상속·저장 방식 등 다양한 옵션을 받아요. 게다가 테이블을 만들면 그 테이블의 한 행에 해당하는 복합 타입(composite type)을 나타내는 데이터 타입도 자동으로 만들어져요. 그래서 같은 스키마 안에서 이미 있는 데이터 타입과 같은 이름을 가진 테이블은 만들 수 없어요.
제약 조건은 새 행이나 갱신된 행이 삽입·갱신에 성공하기 위해 만족해야 하는 검사(test)를 뜻해요. 컬럼 제약 조건과 테이블 제약 조건 두 가지 방식으로 정의할 수 있고, 컬럼 제약 조건은 사실 테이블 제약 조건을 표기만 간단히 바꾼 형태예요.
출처: PostgreSQL 문서
본문
Synopsis
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name ( [
{ column_name data_type [ STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT } ] [ COMPRESSION compression_method ] [ COLLATE collation ] [ column_constraint [ ... ] ]
| table_constraint
| LIKE source_table [ like_option ... ] }
[, ... ]
] )
[ INHERITS ( parent_table [, ... ] ) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name
OF type_name [ (
{ column_name [ WITH OPTIONS ] [ column_constraint [ ... ] ]
| table_constraint }
[, ... ]
) ]
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]
CREATE [ [ GLOBAL | LOCAL ] { TEMPORARY | TEMP } | UNLOGGED ] TABLE [ IF NOT EXISTS ] table_name
PARTITION OF parent_table [ (
{ column_name [ WITH OPTIONS ] [ column_constraint [ ... ] ]
| table_constraint }
[, ... ]
) ] { FOR VALUES partition_bound_spec | DEFAULT }
[ PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [, ... ] ) ]
[ USING method ]
[ WITH ( storage_parameter [= value] [, ... ] ) | WITHOUT OIDS ]
[ ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP } ]
[ TABLESPACE tablespace_name ]
where column_constraint is:
[ CONSTRAINT constraint_name ]
{ NOT NULL [ NO INHERIT ] |
NULL |
CHECK ( expression ) [ NO INHERIT ] |
DEFAULT default_expr |
GENERATED ALWAYS AS ( generation_expr ) [ STORED | VIRTUAL ] |
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ] |
UNIQUE [ NULLS [ NOT ] DISTINCT ] index_parameters |
PRIMARY KEY index_parameters |
REFERENCES reftable [ ( refcolumn ) ] [ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ]
[ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ]
and table_constraint is:
[ CONSTRAINT constraint_name ]
{ CHECK ( expression ) [ NO INHERIT ] |
NOT NULL column_name [ NO INHERIT ] |
UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] [, column_name WITHOUT OVERLAPS ] ) index_parameters |
PRIMARY KEY ( column_name [, ... ] [, column_name WITHOUT OVERLAPS ] ) index_parameters |
EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ] |
FOREIGN KEY ( column_name [, ... ] [, PERIOD column_name ] ) REFERENCES reftable [ ( refcolumn [, ... ] [, PERIOD refcolumn ] ) ]
[ MATCH FULL | MATCH PARTIAL | MATCH SIMPLE ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] }
[ DEFERRABLE | NOT DEFERRABLE ] [ INITIALLY DEFERRED | INITIALLY IMMEDIATE ] [ ENFORCED | NOT ENFORCED ]
and like_option is:
{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
and partition_bound_spec is:
IN ( partition_bound_expr [, ...] ) |
FROM ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] )
TO ( { partition_bound_expr | MINVALUE | MAXVALUE } [, ...] ) |
WITH ( MODULUS numeric_literal, REMAINDER numeric_literal )
index_parameters in UNIQUE, PRIMARY KEY, and EXCLUDE constraints are:
[ INCLUDE ( column_name [, ... ] ) ]
[ WITH ( storage_parameter [= value] [, ... ] ) ]
[ USING INDEX TABLESPACE tablespace_name ]
exclude_element in an EXCLUDE constraint is:
{ column_name | ( expression ) } [ COLLATE collation ] [ opclass [ ( opclass_parameter = value [, ... ] ) ] ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ]
referential_action in a FOREIGN KEY/REFERENCES constraint is:
{ NO ACTION | RESTRICT | CASCADE | SET NULL [ ( column_name [, ... ] ) ] | SET DEFAULT [ ( column_name [, ... ] ) ] }
Description
CREATE TABLE은 현재 데이터베이스에 새롭고 처음엔 비어 있는 테이블을 만들어요. 테이블의 소유자는 명령을 실행한 사용자예요.
스키마 이름을 주면(예: CREATE TABLE myschema.mytable ...) 그 스키마에 테이블이 만들어지고, 생략하면 현재 스키마에 만들어져요. 임시 테이블은 특별한 스키마에 존재하므로 임시 테이블을 만들 때는 스키마 이름을 줄 수 없어요. 테이블 이름은 같은 스키마 안의 다른 릴레이션(테이블·시퀀스·인덱스·뷰·구체화 뷰·외부 테이블)의 이름과 달라야 해요.
CREATE TABLE은 테이블의 한 행에 해당하는 복합 타입을 나타내는 데이터 타입도 자동으로 만들어요. 그래서 테이블은 같은 스키마 안의 기존 데이터 타입과 같은 이름을 가질 수 없어요.
선택적인 제약 조건 절은 삽입·갱신 작업이 성공하려면 새 행이나 갱신된 행이 만족해야 하는 제약(검사)을 지정해요. 제약 조건은 테이블의 유효한 값 집합을 여러 방식으로 정의하는 데 도움을 주는 SQL 객체예요.
제약 조건을 정의하는 방법은 테이블 제약 조건과 컬럼 제약 조건 두 가지가 있어요. 컬럼 제약 조건은 컬럼 정의의 일부로 정의되고, 테이블 제약 조건은 특정 컬럼에 묶이지 않으며 여러 컬럼을 포함할 수 있어요. 모든 컬럼 제약 조건은 테이블 제약 조건으로도 쓸 수 있어요. 컬럼 제약 조건은 단지 제약이 한 컬럼만 다룰 때 쓰는 표기상의 편의일 뿐이에요.
테이블을 만들려면 모든 컬럼 타입(OF 절을 쓰면 그 타입)에 대한 USAGE 권한이 필요해요.
Parameters
TEMPORARY 또는 TEMP
지정하면 테이블이 임시 테이블로 만들어져요. 임시 테이블은 세션이 끝나면 자동으로 삭제되고, 선택적으로 현재 트랜잭션이 끝날 때 삭제할 수도 있어요(아래 ON COMMIT 참고). 기본 search_path는 임시 스키마를 먼저 포함하므로, 임시 테이블이 존재하는 동안은 스키마 한정 이름으로 참조하지 않는 한 같은 이름의 기존 영구 테이블이 새 계획에서 선택되지 않아요. 임시 테이블에 만든 인덱스도 자동으로 임시가 돼요.
autovacuum 데몬은 임시 테이블에 접근할 수 없어서 vacuum이나 analyze를 할 수 없어요. 그래서 적절한 vacuum·analyze 작업은 세션 SQL 명령으로 수행해야 해요. 예를 들어 임시 테이블을 복잡한 쿼리에 쓸 예정이라면, 데이터를 채운 뒤 ANALYZE를 실행해 두는 게 현명해요.
선택적으로 TEMPORARY·TEMP 앞에 GLOBAL이나 LOCAL을 쓸 수 있어요. 현재 PostgreSQL에서는 이게 아무런 차이를 만들지 않고, 더 이상 쓰지 않는(deprecated) 문법이에요. Compatibility를 참고하세요.
UNLOGGED
지정하면 테이블이 unlogged 테이블로 만들어져요. unlogged 테이블에 쓴 데이터는 write-ahead 로그에 기록되지 않아서(28장 참고) 일반 테이블보다 훨씬 빠르지만, 충돌에 안전하지 않아요. unlogged 테이블은 크래시나 비정상 종료 후 자동으로 잘려나가요(truncate). unlogged 테이블의 내용은 스탠바이 서버에 복제되지도 않아요. unlogged 테이블에 만든 인덱스도 자동으로 unlogged가 돼요.
이걸 지정하면 unlogged 테이블과 함께 만들어진 시퀀스(identity나 serial 컬럼용)도 unlogged로 만들어져요.
이 형태는 파티션 테이블에 지원되지 않아요.
IF NOT EXISTS
같은 이름의 릴레이션이 이미 있어도 오류를 내지 않아요. 이 경우 통지(notice)가 발생할 뿐이에요. 기존 릴레이션이 만들려던 것과 비슷하다는 보장은 없다는 점을 유의하세요.
table_name
만들 테이블의 이름(스키마 한정 가능)이에요.
OF type_name
지정한 독립형 복합 타입(즉 CREATE TYPE으로 만든 타입)에서 구조를 가져오는 형식 테이블(typed table)을 만들어요. 그래도 새 복합 타입도 함께 만들어져요. 테이블은 참조한 타입에 의존하게 되어, 그 타입에 대한 계단식(cascaded) 변경·삭제가 테이블까지 전파돼요.
형식 테이블은 항상 파생된 타입과 같은 컬럼 이름·데이터 타입을 가지므로 추가 컬럼을 지정할 수 없어요. 하지만 CREATE TABLE 명령으로 테이블에 기본값·제약 조건을 추가하고 저장 매개변수를 지정할 수는 있어요.
column_name
새 테이블에 만들 컬럼의 이름이에요.
data_type
컬럼의 데이터 타입이에요. 배열 지정자(array specifier)를 포함할 수 있어요. PostgreSQL이 지원하는 데이터 타입에 대한 자세한 내용은 8장을 참고하세요.
COLLATE collation
COLLATE 절은 컬럼에 콜레이션을 지정해요(컬럼은 콜레이션 가능한 데이터 타입이어야 해요). 지정하지 않으면 컬럼 데이터 타입의 기본 콜레이션이 사용돼요.
STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN | DEFAULT }
이 형태는 컬럼의 저장 방식을 설정해요. 이 컬럼을 인라인으로 유지할지 2차 TOAST 테이블에 둘지, 데이터를 압축할지 말지를 제어하죠. PLAIN은 integer 같은 고정 길이 값에 써야 하고 인라인·비압축이에요. MAIN은 인라인·압축 가능한 데이터용이고, EXTERNAL은 외부·비압축 데이터, EXTENDED는 외부·압축 데이터용이에요. DEFAULT를 쓰면 컬럼 데이터 타입의 기본 저장 방식으로 설정돼요. EXTENDED는 비-PLAIN 저장을 지원하는 대부분의 데이터 타입의 기본값이에요. EXTERNAL을 쓰면 아주 큰 text·bytea 값에 대한 부분 문자열 연산이 더 빨라지지만 저장 공간은 더 많이 차지해요. 자세한 내용은 66.2절을 참고하세요.
COMPRESSION compression_method
COMPRESSION 절은 컬럼의 압축 방식을 설정해요. 압축은 가변 폭 데이터 타입에만 지원되고, 컬럼의 저장 방식이 main이나 extended일 때만 사용돼요. (컬럼 저장 방식에 대한 정보는 ALTER TABLE 참고.) 파티션 테이블에 이 속성을 설정해도 직접적인 효과는 없어요. 그런 테이블은 자신의 저장 공간이 없기 때문이죠. 다만 설정된 값은 새로 만들어지는 파티션에 상속돼요. 지원하는 압축 방식은 pglz와 lz4예요. (lz4는 PostgreSQL을 빌드할 때 --with-lz4를 쓴 경우에만 쓸 수 있어요.) 또한 compression_method를 default로 두면 기본 동작을 명시적으로 지정하는데, 데이터 삽입 시점의 default_toast_compression 설정을 참고해 방식을 정해요.
INHERITS ( parent_table [, ... ] )
선택적인 INHERITS 절은 새 테이블이 모든 컬럼을 자동으로 상속받을 테이블 목록을 지정해요. 부모 테이블은 일반 테이블이나 외부 테이블일 수 있어요.
INHERITS를 쓰면 새 자식 테이블과 부모 테이블(들) 사이에 지속적인 관계가 만들어져요. 부모에 대한 스키마 변경은 보통 자식에도 전파되고, 기본적으로 자식 테이블의 데이터는 부모(들)의 스캔에 포함돼요.
같은 컬럼 이름이 둘 이상의 부모 테이블에 있으면, 각 부모 테이블에서 컬럼의 데이터 타입이 일치하지 않으면 오류가 나요. 충돌이 없으면 중복 컬럼이 합쳐져 새 테이블의 단일 컬럼이 돼요. 새 테이블의 컬럼 이름 목록에 상속되는 이름도 있으면 그 데이터 타입도 상속된 컬럼과 일치해야 하고, 컬럼 정의가 하나로 합쳐져요. 새 테이블이 그 컬럼의 기본값을 명시적으로 지정하면 그 기본값이 상속 선언의 기본값들을 덮어써요. 그렇지 않으면 그 컬럼에 기본값을 지정한 모든 부모가 같은 기본값을 지정해야 하고, 그렇지 않으면 오류가 나요.
CHECK 제약 조건은 컬럼과 본질적으로 같은 방식으로 합쳐져요. 여러 부모 테이블·그리고/또는 새 테이블 정의에 같은 이름의 CHECK 제약 조건이 있으면 모두 같은 검사 표현식을 가져야 하고, 그렇지 않으면 오류가 나요. 같은 이름·같은 표현식의 제약은 하나의 복사본으로 합쳐져요. 부모에서 NO INHERIT로 표시된 제약은 고려되지 않아요. 새 테이블의 이름 없는 CHECK 제약은 항상 고유한 이름이 선택되므로 합쳐지지 않는다는 점을 유의하세요.
컬럼 STORAGE 설정도 부모 테이블에서 복사돼요.
부모 테이블의 컬럼이 identity 컬럼이어도 그 속성은 상속되지 않아요. 원하면 자식 테이블의 컬럼을 identity 컬럼으로 선언할 수 있어요.
PARTITION BY { RANGE | LIST | HASH } ( { column_name | ( expression ) } [ opclass ] [, ...] )
선택적인 PARTITION BY 절은 테이블을 파티셔닝할 전략을 지정해요. 이렇게 만든 테이블을 파티션 테이블(partitioned table)이라고 불러요. 괄호 안의 컬럼·표현식 목록이 테이블의 파티션 키가 돼요. 범위·해시 파티셔닝을 쓸 때 파티션 키는 여러 컬럼이나 표현식을 포함할 수 있지만(최대 32개, PostgreSQL을 빌드할 때 이 제한을 바꿀 수 있음), 리스트 파티셔닝의 파티션 키는 단일 컬럼이나 표현식이어야 해요.
범위·리스트 파티셔닝은 btree 연산자 클래스를 필요로 하고, 해시 파티셔닝은 해시 연산자 클래스를 필요로 해요. 연산자 클래스를 명시하지 않으면 적절한 타입의 기본 연산자 클래스가 사용되고, 기본 연산자 클래스가 없으면 오류가 나요. 해시 파티셔닝을 쓸 때 사용하는 연산자 클래스는 지원 함수 2(support function 2)를 구현해야 해요(36.16.3절 참고).
파티션 테이블은 별도의 CREATE TABLE 명령으로 만들어지는 서브 테이블(파티션)들로 나뉘어요. 파티션 테이블 자체는 비어 있어요. 테이블에 삽입된 데이터 행은 파티션 키의 컬럼·표현식 값에 따라 파티션으로 라우팅돼요. 새 행의 값과 일치하는 기존 파티션이 없으면 오류가 나요.
테이블 파티셔닝에 대한 더 자세한 논의는 5.12절을 참고하세요.
PARTITION OF parent_table { FOR VALUES partition_bound_spec | DEFAULT }
테이블을 지정한 부모 테이블의 파티션으로 만들어요. FOR VALUES로 특정 값에 대한 파티션으로 만들거나 DEFAULT로 기본 파티션으로 만들 수 있어요. 부모 테이블에 있는 인덱스·제약 조건·사용자 정의 행 수준 트리거는 새 파티션에 복제돼요.
partition_bound_spec은 부모 테이블의 파티셔닝 방식과 파티션 키에 맞아야 하고, 그 부모의 기존 파티션과 겹치면 안 돼요. IN 형태는 리스트 파티셔닝에, FROM·TO 형태는 범위 파티셔닝에, WITH 형태는 해시 파티셔닝에 쓰여요.
partition_bound_expr은 변수가 없는 표현식이에요(서브쿼리·창 함수·집계 함수·집합 반환 함수는 허용되지 않아요). 데이터 타입은 해당 파티션 키 컬럼의 데이터 타입과 일치해야 해요. 표현식은 테이블 생성 시점에 한 번 평가되므로 CURRENT_TIMESTAMP 같은 휘발성 표현식도 넣을 수 있어요.
리스트 파티션을 만들 때 NULL을 지정하면 그 파티션이 파티션 키 컬럼을 null로 허용한다는 뜻이에요. 다만 주어진 부모 테이블에 그런 리스트 파티션은 하나만 있을 수 있어요. 범위 파티션에는 NULL을 지정할 수 없어요.
범위 파티션을 만들 때 FROM으로 지정한 하한은 포함(inclusive) 경계이고, TO로 지정한 상한은 배제(exclusive) 경계예요. 즉 FROM 목록의 값은 이 파티션의 해당 파티션 키 컬럼에 유효한 값이지만, TO 목록의 값은 아닌 거죠. 이 문장은 행 단위 비교(9.25.5절) 규칙에 따라 이해해야 해요. 예를 들어 PARTITION BY RANGE (x,y)에서 경계 FROM (1, 2) TO (3, 4)는 x=1과 임의의 y>=2, x=2와 임의의 null 아닌 y, x=3과 임의의 y<4를 허용해요.
특수 값 MINVALUE와 MAXVALUE는 범위 파티션을 만들 때 컬럼 값에 하한·상한이 없음을 나타내는 데 쓸 수 있어요. 예를 들어 FROM (MINVALUE) TO (10)으로 정의한 파티션은 10보다 작은 모든 값을 허용하고, FROM (10) TO (MAXVALUE)로 정의한 파티션은 10보다 크거나 같은 모든 값을 허용해요.
둘 이상의 컬럼이 있는 범위 파티션을 만들 때는 하한의 일부로 MAXVALUE를, 상한의 일부로 MINVALUE를 쓰는 것도 의미가 있어요. 예를 들어 FROM (0, MAXVALUE) TO (10, MAXVALUE)로 정의한 파티션은 첫 번째 파티션 키 컬럼이 0보다 크고 10 이하인 모든 행을 허용해요. 비슷하게 FROM ('a', MINVALUE) TO ('b', MINVALUE)로 정의한 파티션은 첫 번째 파티션 키 컬럼이 "a"로 시작하는 모든 행을 허용해요.
파티셔닝 경계의 한 컬럼에 MINVALUE나 MAXVALUE를 쓰면, 이후의 모든 컬럼에도 같은 값을 써야 해요. 예를 들어 (10, MINVALUE, 0)은 유효한 경계가 아니고 (10, MINVALUE, MINVALUE)라고 써야 해요.
또한 timestamp 같은 일부 요소 타입은 "무한대(infinity)"라는 개념이 있는데, 그냥 저장할 수 있는 값 하나일 뿐이에요. 이것은 실제로 저장할 수 있는 값이 아니라 값이 무한하다는 뜻을 나타내는 MINVALUE·MAXVALUE와는 달라요. MAXVALUE는 "무한대"를 포함한 어떤 값보다도 크다고 생각할 수 있고, MINVALUE는 "마이너스 무한대"를 포함한 어떤 값보다도 작다고 생각할 수 있어요. 그래서 범위 FROM ('infinity') TO (MAXVALUE)는 빈 범위가 아니라 정확히 하나의 값 "infinity"를 저장할 수 있게 해 줘요.
DEFAULT를 지정하면 테이블이 부모 테이블의 기본 파티션으로 만들어져요. 이 옵션은 해시 파티션 테이블에는 쓸 수 없어요. 주어진 부모의 다른 파티션에 맞지 않는 파티션 키 값은 기본 파티션으로 라우팅돼요.
테이블에 기존 DEFAULT 파티션이 있는데 새 파티션이 추가되면, 기본 파티션을 스캔해서 새 파티션에 진짜 속하는 행이 없는지 확인해야 해요. 기본 파티션에 행이 많으면 이게 느릴 수 있어요. 기본 파티션이 외부 테이블이거나, 새 파티션에 놓여야 할 행을 가질 수 없다는 것을 증명하는 제약 조건이 있으면 스캔은 건너뛰어져요.
해시 파티션을 만들 때는 modulus와 remainder를 지정해야 해요. modulus는 양의 정수, remainder는 modulus보다 작은 음이 아닌 정수여야 해요. 보통 해시 파티션 테이블을 처음 설정할 때는 modulus를 파티션 수와 같게 하고 모든 테이블에 같은 modulus와 서로 다른 remainder를 지정해요(아래 예시 참고). 하지만 모든 파티션이 같은 modulus를 가져야 하는 건 아니에요. 단지 해시 파티션 테이블의 파티션들에 나타나는 각 modulus가 다음으로 큰 modulus의 인수여야 할 뿐이에요. 이 덕분에 모든 데이터를 한 번에 옮기지 않고도 파티션 수를 점진적으로 늘릴 수 있어요. 예를 들어 modulus가 8인 파티션 8개를 가진 해시 파티션 테이블이 있는데 파티션 수를 16으로 늘려야 한다고 해 볼게요. modulus-8 파티션 하나를 분리(detach)하고, 키 공간의 같은 부분을 덮는 modulus-16 파티션 두 개를 만들어요(하나는 분리한 파티션의 remainder와 같은 remainder, 다른 하나는 그 값에 8을 더한 remainder). 그리고 그 데이터로 다시 채우면 돼요. 그런 다음 modulus-8 파티션이 남지 않을 때까지 — 나중에 — 모드-8 파티션마다 이걸 반복할 수 있어요. 각 단계에서 여전히 많은 데이터 이동이 있을 수 있지만, 통째로 새 테이블을 만들고 모든 데이터를 한 번에 옮기는 것보다는 나아요.
파티션은 속한 파티션 테이블과 같은 컬럼 이름·타입을 가져야 해요. 파티션 테이블의 컬럼 이름·타입을 바꾸면 모든 파티션에 자동으로 전파돼요. CHECK 제약 조건은 모든 파티션에 자동으로 상속되지만, 개별 파티션은 추가 CHECK 제약 조건을 지정할 수 있어요. 부모와 같은 이름·조건의 추가 제약은 부모 제약 조건과 합쳐져요. 기본값은 각 파티션에 별도로 지정할 수 있어요. 다만 파티션 테이블을 통해 튜플을 삽입할 때는 파티션의 기본값이 적용되지 않는다는 점을 유의하세요.
파티션 테이블에 삽입된 행은 자동으로 올바른 파티션으로 라우팅돼요. 적절한 파티션이 없으면 오류가 발생해요.
TRUNCATE처럼 보통 테이블과 그 상속 자식 모두에 영향을 주는 작업은 모든 파티션으로 계단식으로 전파되지만, 개별 파티션에 대해서만 수행할 수도 있어요.
PARTITION OF로 파티션을 만들려면 부모 파티션 테이블에 ACCESS EXCLUSIVE 잠금을 걸어야 해요. 마찬가지로 DROP TABLE로 파티션을 제거하려면 부모 테이블에 ACCESS EXCLUSIVE 잠금이 필요해요. ALTER TABLE ATTACH/DETACH PARTITION을 쓰면 더 약한 잠금으로 이런 작업을 수행해 파티션 테이블에 대한 동시 작업과의 간섭을 줄일 수 있어요.
LIKE source_table [ like_option ... ]
LIKE 절은 새 테이블이 모든 컬럼 이름·데이터 타입·NOT NULL 제약 조건을 자동으로 복사할 테이블을 지정해요.
INHERITS와 달리, 생성이 끝나면 새 테이블과 원래 테이블은 완전히 분리돼요. 원래 테이블의 변경은 새 테이블에 적용되지 않고, 원래 테이블의 스캔에 새 테이블의 데이터를 포함시킬 수도 없어요.
또한 INHERITS와 달리 LIKE로 복사한 컬럼·제약 조건은 같은 이름의 컬럼·제약 조건과 합쳐지지 않아요. 같은 이름을 명시적으로 또는 다른 LIKE 절에서 지정하면 오류가 나요.
선택적인 like_option 절은 원래 테이블의 어떤 추가 속성을 복사할지 지정해요. INCLUDING을 지정하면 속성을 복사하고, EXCLUDING을 지정하면 생략해요. 기본값은 EXCLUDING이에요. 같은 종류의 객체에 대해 여러 번 지정하면 마지막 것이 사용돼요. 사용 가능한 옵션은 다음과 같아요.
INCLUDING COMMENTS
복사된 컬럼, 체크 제약 조건, NOT NULL 제약 조건, 인덱스, 확장 통계에 대한 주석이 복사돼요. 기본 동작은 주석을 제외하는 것이고, 그래서 새 테이블의 해당 객체들은 주석이 없어요.
INCLUDING COMPRESSION
컬럼의 압축 방식이 복사돼요. 기본 동작은 압축 방식을 제외해서 컬럼이 기본 압축 방식을 갖게 해요.
INCLUDING CONSTRAINTS
CHECK 제약 조건이 복사돼요. 컬럼 제약 조건과 테이블 제약 조건을 구분하지 않아요. NOT NULL 제약 조건은 항상 새 테이블에 복사돼요.
INCLUDING DEFAULTS
복사된 컬럼 정의의 기본 표현식이 복사돼요. 그렇지 않으면 기본 표현식이 복사되지 않아서 새 테이블의 복사된 컬럼들이 null 기본값을 갖게 돼요. nextval 같은 데이터베이스 수정 함수를 호출하는 기본값을 복사하면 원래 테이블과 새 테이블 사이에 기능적 연결이 생길 수 있다는 점을 유의하세요.
INCLUDING GENERATED
복사된 컬럼 정의의 생성 표현식과 stored/virtual 선택이 복사돼요. 기본적으로 새 컬럼은 일반 베이스 컬럼이 돼요.
INCLUDING IDENTITY
복사된 컬럼 정의의 identity 지정이 복사돼요. 새 테이블의 각 identity 컬럼에 대해, 이전 테이블과 연결된 시퀀스와는 별개의 새 시퀀스가 만들어져요.
INCLUDING INDEXES
원래 테이블의 인덱스, PRIMARY KEY, UNIQUE, EXCLUDE 제약 조건이 새 테이블에 만들어져요. 새 인덱스·제약 조건의 이름은 원본의 이름과 무관하게 기본 규칙에 따라 정해져요. (이 동작은 새 인덱스의 가능한 이름 중복 실패를 피해 줘요.)
INCLUDING STATISTICS
확장 통계가 새 테이블에 복사돼요.
INCLUDING STORAGE
복사된 컬럼 정의의 STORAGE 설정이 복사돼요. 기본 동작은 STORAGE 설정을 제외해서 새 테이블의 복사된 컬럼이 타입별 기본 설정을 갖게 해요. STORAGE 설정에 대한 자세한 내용은 66.2절을 참고하세요.
INCLUDING ALL
INCLUDING ALL은 사용 가능한 개별 옵션을 모두 선택하는 약식 형태예요. (특정 옵션 몇 개만 제외하고 모두 선택하려면 INCLUDING ALL 뒤에 개별 EXCLUDING 절을 쓰면 유용할 수 있어요.)
LIKE 절은 뷰, 외부 테이블, 복합 타입에서 컬럼 정의를 복사하는 데도 쓸 수 있어요. 적용할 수 없는 옵션(예: 뷰에서 INCLUDING INDEXES)은 무시돼요.
CONSTRAINT constraint_name
컬럼·테이블 제약 조건의 선택적 이름이에요. 제약이 위반되면 그 이름이 오류 메시지에 나타나므로, col must be positive 같은 제약 이름으로 클라이언트 애플리케이션에 유용한 제약 정보를 전달할 수 있어요. (공백을 포함한 제약 이름을 지정하려면 큰따옴표가 필요해요.) 제약 이름을 지정하지 않으면 시스템이 이름을 생성해요.
NOT NULL [ NO INHERIT ]
컬럼이 null 값을 포함할 수 없어요.
NO INHERIT로 표시된 제약은 자식 테이블에 전파되지 않아요.
NULL
컬럼이 null 값을 포함할 수 있어요. 기본값이에요.
이 절은 비표준 SQL 데이터베이스와의 호환성을 위해 제공될 뿐이에요. 새 애플리케이션에서 쓰는 것은 권장하지 않아요.
CHECK ( expression ) [ NO INHERIT ]
CHECK 절은 삽입·갱신 작업이 성공하려면 새 행이나 갱신된 행이 만족해야 하는, Boolean 결과를 내는 표현식을 지정해요. TRUE나 UNKNOWN으로 평가되는 표현식은 성공해요. 삽입·갱신 작업의 어떤 행이 FALSE 결과를 내면 오류 예외가 발생하고, 삽입·갱신은 데이터베이스를 바꾸지 않아요. 컬럼 제약 조건으로 지정된 체크 제약 조건은 그 컬럼의 값만 참조해야 하지만, 테이블 제약 조건에 나타나는 표현식은 여러 컬럼을 참조할 수 있어요.
현재 CHECK 표현식은 서브쿼리를 포함할 수 없고, 현재 행의 컬럼 외의 변수를 참조할 수 없어요(5.5.1절 참고). 시스템 컬럼 tableoid는 참조할 수 있지만 다른 시스템 컬럼은 참조할 수 없어요.
NO INHERIT로 표시된 제약은 자식 테이블에 전파되지 않아요.
테이블에 CHECK 제약 조건이 여러 개 있으면, NOT NULL 제약 조건을 확인한 뒤 이름의 알파벳 순으로 각 행마다 검사돼요. (9.5 이전 PostgreSQL 버전은 CHECK 제약 조건의 발화 순서를 준수하지 않았어요.)
DEFAULT default_expr
DEFAULT 절은 컬럼 정의가 나타나는 컬럼의 기본 데이터 값을 지정해요. 값은 변수가 없는 표현식이에요(특히 현재 테이블의 다른 컬럼에 대한 교차 참조는 허용되지 않아요). 서브쿼리도 허용되지 않아요. 기본 표현식의 데이터 타입은 컬럼의 데이터 타입과 일치해야 해요.
기본 표현식은 컬럼 값을 지정하지 않는 삽입 작업에서 사용돼요. 컬럼에 기본값이 없으면 기본값은 null이에요.
GENERATED ALWAYS AS ( generation_expr ) [ STORED | VIRTUAL ]
이 절은 컬럼을 생성 컬럼(generated column)으로 만들어요. 이 컬럼에는 쓸 수 없고, 읽으면 지정된 표현식의 결과가 반환돼요.
VIRTUAL을 지정하면 컬럼은 읽을 때 계산되고 저장 공간을 차지하지 않아요. STORED를 지정하면 컬럼은 쓸 때 계산되고 디스크에 저장돼요. VIRTUAL이 기본값이에요.
생성 표현식은 테이블의 다른 컬럼을 참조할 수 있지만, 다른 생성 컬럼은 참조할 수 없어요. 사용하는 함수·연산자는 모두 불변(immutable)이어야 해요. 다른 테이블에 대한 참조는 허용되지 않아요.
가상 생성 컬럼은 사용자 정의 타입을 가질 수 없고, 가상 생성 컬럼의 생성 표현식은 사용자 정의 함수나 타입을 참조할 수 없어요. 즉 내장 함수·타입만 쓸 수 있어요. 이 제한은 연산자나 캐스트의 기반이 되는 함수·타입처럼 간접적인 경우에도 적용돼요. (이 제한은 stored 생성 컬럼에는 없어요.)
GENERATED { ALWAYS | BY DEFAULT } AS IDENTITY [ ( sequence_options ) ]
이 절은 컬럼을 identity 컬럼으로 만들어요. 암묵적 시퀀스가 연결되고, 새로 삽입된 행의 컬럼은 시퀀스에서 자동으로 값을 받아요. 이런 컬럼은 암묵적으로 NOT NULL이에요.
ALWAYS와 BY DEFAULT 절은 INSERT·UPDATE 명령에서 사용자가 명시적으로 지정한 값을 어떻게 처리할지를 결정해요.
INSERT 명령에서 ALWAYS를 선택하면, 사용자 지정 값은 INSERT 문이 OVERRIDING SYSTEM VALUE를 지정한 경우에만 받아들여져요. BY DEFAULT를 선택하면 사용자 지정 값이 우선해요. 자세한 내용은 INSERT를 참고하세요. (COPY 명령에서는 이 설정과 무관하게 사용자 지정 값이 항상 사용돼요.)
UPDATE 명령에서 ALWAYS를 선택하면 컬럼을 DEFAULT가 아닌 어떤 값으로든 갱신하는 것은 거부돼요. BY DEFAULT를 선택하면 컬럼을 정상적으로 갱신할 수 있어요. (UPDATE 명령에는 OVERRIDING 절이 없어요.)
선택적인 sequence_options 절은 시퀀스의 매개변수를 덮어쓰는 데 쓸 수 있어요. 사용 가능한 옵션에는 CREATE SEQUENCE에 나온 것들에 더해 SEQUENCE NAME name, LOGGED, UNLOGGED가 있는데, 시퀀스의 이름과 지속성 수준을 선택할 수 있게 해 줘요. SEQUENCE NAME이 없으면 시스템이 사용하지 않는 이름을 골라요. LOGGED·UNLOGGED가 없으면 시퀀스는 테이블과 같은 지속성 수준을 가져요.
UNIQUE [ NULLS [ NOT ] DISTINCT ] (컬럼 제약) UNIQUE [ NULLS [ NOT ] DISTINCT ] ( column_name [, ... ] [, column_name WITHOUT OVERLAPS ] ) [ INCLUDE ( column_name [, ...]) ] (테이블 제약)
UNIQUE 제약 조건은 테이블의 한 개 이상의 컬럼 그룹이 유일한 값만 가질 수 있음을 지정해요. 테이블 unique 제약 조건의 동작은 컬럼 unique 제약 조건과 같지만, 여러 컬럼에 걸칠 수 있는 추가 능력이 있어요. 따라서 이 제약은 어떤 두 행도 이 컬럼들 중 적어도 하나에서 달라야 함을 강제해요.
마지막 컬럼에 WITHOUT OVERLAPS 옵션을 지정하면 그 컬럼은 동등 비교 대신 겹침(overlap)을 검사해요. 그 경우 제약의 다른 컬럼들은 WITHOUT OVERLAPS 컬럼에서 겹치지 않는 한 중복을 허용해요. (이 컬럼이 날짜·타임스탬프 범위라면 이를 시간 키(temporal key)라고 부르기도 하지만, PostgreSQL은 어떤 베이스 타입에 대한 범위도 허용해요.) 실제로 이런 제약은 UNIQUE 제약 조건이 아니라 EXCLUDE 제약 조건으로 강제돼요. 예를 들어 UNIQUE (id, valid_at WITHOUT OVERLAPS)는 EXCLUDE USING GIST (id WITH =, valid_at WITH &&)처럼 동작해요. WITHOUT OVERLAPS 컬럼은 범위·멀티범위 타입이어야 해요. 빈 범위·멀티범위는 허용되지 않아요. 제약의 비-WITHOUT OVERLAPS 컬럼은 GiST 인덱스에서 동등 비교할 수 있는 어떤 타입이든 될 수 있어요. 기본적으로 범위 타입만 지원하지만, btree_gist 확장을 추가하면 다른 타입도 쓸 수 있어요(이 기능을 쓰는 예상 방식이에요).
unique 제약 조건의 목적상 null 값은 같다고 간주되지 않아요. 단 NULLS NOT DISTINCT를 지정한 경우는 달라요.
각 unique 제약 조건은 테이블에 정의된 다른 unique·primary key 제약 조건이 이름을 붙인 컬럼 집합과 다른 컬럼 집합을 이름 붙여야 해요. (그렇지 않으면 중복 unique 제약 조건이 버려져요.)
다단계 파티션 계층에 unique 제약 조건을 만들 때는, 대상 파티션 테이블의 파티션 키와 모든 하위 파티션 테이블의 모든 컬럼이 제약 정의에 포함되어야 해요.
unique 제약 조건을 추가하면 제약에 사용된 컬럼·컬럼 그룹에 unique btree 인덱스가 자동으로 만들어져요. 하지만 제약이 WITHOUT OVERLAPS 절을 포함하면 GiST 인덱스를 사용해요. 만들어진 인덱스는 unique 제약 조건과 같은 이름을 가져요.
선택적인 INCLUDE 절은 그 인덱스에 "페이로드"일 뿐인 컬럼을 하나 이상 추가해요. 그 컬럼에는 유일성이 강제되지 않고, 그 컬럼을 기준으로 인덱스를 검색할 수도 없어요. 다만 인덱스 전용 스캔으로는 검색할 수 있어요. 포함된 컬럼에 제약이 강제되지는 않아도 여전히 의존한다는 점을 유의하세요. 그래서 그런 컬럼에 대한 일부 작업(예: DROP COLUMN)이 제약·인덱스의 계단식 삭제를 일으킬 수 있어요.
PRIMARY KEY (컬럼 제약) PRIMARY KEY ( column_name [, ... ] [, column_name WITHOUT OVERLAPS ] ) [ INCLUDE ( column_name [, ...]) ] (테이블 제약)
PRIMARY KEY 제약 조건은 테이블의 한 개 이상의 컬럼이 유일한(중복 없는) null 아닌 값만 가질 수 있음을 지정해요. 컬럼 제약이든 테이블 제약이든 테이블에는 primary key를 하나만 지정할 수 있어요.
primary key 제약 조건은 같은 테이블에 정의된 어떤 unique 제약 조건이 이름 붙인 컬럼 집합과 다른 컬럼 집합을 이름 붙여야 해요. (그렇지 않으면 unique 제약이 중복이 되어 버려져요.)
PRIMARY KEY는 UNIQUE와 NOT NULL의 조합과 같은 데이터 제약을 강제해요. 다만 컬럼 집합을 primary key로 식별하면 스키마 설계에 대한 메타데이터도 제공해요. primary key는 다른 테이블이 이 컬럼 집합을 행의 유일 식별자로 신뢰할 수 있음을 의미하기 때문이죠.
파티션 테이블에 두면 PRIMARY KEY 제약 조건은 앞서 UNIQUE 제약 조건에 대해 설명한 제한을 공유해요.
PRIMARY KEY 제약 조건을 추가하면 제약에 사용된 컬럼·컬럼 그룹에 unique btree 인덱스가 자동으로 만들어지고, WITHOUT OVERLAPS를 지정했으면 GiST를 사용해요.
선택적인 INCLUDE 절은 그 인덱스에 "페이로드"일 뿐인 컬럼을 하나 이상 추가해요. 그 컬럼에는 유일성이 강제되지 않고, 그 컬럼을 기준으로 인덱스를 검색할 수도 없어요. 다만 인덱스 전용 스캔으로는 검색할 수 있어요. 포함된 컬럼에 제약이 강제되지는 않아도 여전히 의존한다는 점을 유의하세요. 그래서 그런 컬럼에 대한 일부 작업(예: DROP COLUMN)이 제약·인덱스의 계단식 삭제를 일으킬 수 있어요.
EXCLUDE [ USING index_method ] ( exclude_element WITH operator [, ... ] ) index_parameters [ WHERE ( predicate ) ]
EXCLUDE 절은 배제 제약 조건(exclusion constraint)을 정의해요. 지정한 컬럼·표현식을 지정한 연산자로 비교할 때 어떤 두 행이든 그 비교가 모두 TRUE를 반환하지 않음을 보장하죠. 지정한 연산자가 모두 동등을 검사하면 이는 UNIQUE 제약 조건과 동등하지만, 일반 unique 제약 조건이 더 빨라요. 다만 배제 제약 조건은 단순 동등보다 더 일반적인 제약을 지정할 수 있어요. 예를 들어 && 연산자를 사용해 테이블의 어떤 두 행도 겹치는 원(circle)을 포함하지 않도록(8.8절 참고) 제약을 지정할 수 있어요. 연산자는 교환 법칙이 성립해야 해요.
배제 제약 조건은 제약과 같은 이름의 인덱스를 사용해 구현돼요. 그래서 지정된 각 연산자는 인덱스 접근 방식 index_method에 대한 적절한 연산자 클래스(11.10절 참고)와 연결되어야 해요. 각 exclude_element는 인덱스의 한 컬럼을 정의하므로, 선택적으로 콜레이션·연산자 클래스·연산자 클래스 매개변수·정렬 옵션을 지정할 수 있어요. 이들은 CREATE INDEX에서 자세히 설명해요.
접근 방식은 amgettuple을 지원해야 해요(63장 참고). 현재로선 GIN을 쓸 수 없다는 뜻이에요. B-트리·해시 인덱스를 배제 제약 조건과 쓰는 것은 허용되지만 의미가 거의 없어요. 일반 unique 제약 조건이 더 잘 해 주는 일을 아무것도 하지 않기 때문이죠. 그래서 실제로 접근 방식은 항상 GiST나 SP-GiST예요.
predicate로 테이블의 부분 집합에 대한 배제 제약 조건을 지정할 수 있어요. 내부적으로 이는 부분 인덱스를 만들어요. predicate 주변에 괄호가 필요하다는 점을 유의하세요.
다단계 파티션 계층에 배제 제약 조건을 만들 때는, 대상 파티션 테이블의 파티션 키와 모든 하위 파티션 테이블의 모든 컬럼이 제약 정의에 포함되어야 해요. 또한 그 컬럼들은 동등 연산자로 비교되어야 해요. 이 제한들은 잠재적으로 충돌하는 행이 같은 파티션에 존재하게 보장해요. 제약은 파티션 키의 일부가 아닌 다른 컬럼도 참조할 수 있는데, 그런 컬럼은 적절한 연산자로 비교할 수 있어요.
REFERENCES reftable [ ( refcolumn ) ] [ MATCH matchtype ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] (컬럼 제약) FOREIGN KEY ( column_name [, ... ] [, PERIOD column_name ] ) REFERENCES reftable [ ( refcolumn [, ... ] [, PERIOD refcolumn ] ) ] [ MATCH matchtype ] [ ON DELETE referential_action ] [ ON UPDATE referential_action ] (테이블 제약)
이 절들은 외래 키 제약 조건을 지정해요. 새 테이블의 한 개 이상의 컬럼 그룹이 참조 테이블의 어떤 행의 참조 컬럼 값과 일치하는 값만 포함하도록 요구하죠. refcolumn 목록을 생략하면 reftable의 primary key가 사용돼요. 그렇지 않으면 refcolumn 목록은 비지연(non-deferrable) unique·primary key 제약의 컬럼을 가리키거나, 비부분 unique 인덱스의 컬럼이어야 해요.
마지막 컬럼이 PERIOD로 표시되면 특별한 방식으로 취급돼요. 비-PERIOD 컬럼은 동등 비교되는 동안(그리고 그 중 적어도 하나는 있어야 하는데) PERIOD 컬럼은 동등 비교되지 않아요. 대신 참조 테이블에 (키의 비-PERIOD 부분을 기준으로) 일치하는 레코드가 있어서 그 결합된 PERIOD 값이 참조하는 레코드의 것을 완전히 덮으면 제약이 만족된 것으로 간주돼요. 다시 말해 참조는 그 전체 기간 동안 referent가 있어야 해요. 이 컬럼은 범위·멀티범위 타입이어야 해요. 또한 참조 테이블은 WITHOUT OVERLAPS로 선언된 primary key·unique 제약이 있어야 해요. 마지막으로 외래 키가 PERIOD column_name 지정을 가지면 해당 refcolumn도, 있으면 PERIOD로 표시되어야 해요. refcolumn 절을 생략해서 reftable의 primary key 제약을 고르면, 그 primary key의 마지막 컬럼이 WITHOUT OVERLAPS로 표시되어야 해요.
참조·피참조 컬럼의 각 쌍에 대해, 둘 다 콜레이션 가능한 데이터 타입이면 콜레이션이 둘 다 결정적(deterministic)이거나 둘 다 같아야 해요. 이는 두 컬럼이 일관된 동등 개념을 가지도록 보장해요.
사용자는 참조 테이블(전체 또는 특정 참조 컬럼)에 대한 REFERENCES 권한이 있어야 해요. 외래 키 제약 조건을 추가하려면 참조 테이블에 SHARE ROW EXCLUSIVE 잠금이 필요해요. 외래 키 제약 조건은 임시 테이블과 영구 테이블 사이에 정의할 수 없다는 점을 유의하세요.
참조 컬럼에 삽입된 값은 주어진 match type을 사용해 참조 테이블·참조 컬럼의 값과 대조돼요. match type은 MATCH FULL, MATCH PARTIAL, MATCH SIMPLE(기본값) 세 가지가 있어요. MATCH FULL은 다중 컬럼 외래 키의 한 컬럼이, 모든 외래 키 컬럼이 null이 아닌 한 null이 되도록 허용하지 않아요. 모두 null이면 행이 참조 테이블에서 일치할 필요가 없어요. MATCH SIMPLE은 외래 키 컬럼 중 아무거나 null을 허용하고, 그중 하나라도 null이면 행이 참조 테이블에서 일치할 필요가 없어요. MATCH PARTIAL은 아직 구현되지 않았어요. (물론 NOT NULL 제약 조건을 참조 컬럼에 적용하면 이런 경우를 막을 수 있어요.)
또한 참조 컬럼의 데이터가 바뀌면 이 테이블의 컬럼 데이터에 특정 작업이 수행돼요. ON DELETE 절은 참조 테이블의 참조 행이 삭제될 때 수행할 작업을 지정해요. 마찬가지로 ON UPDATE 절은 참조 테이블의 참조 컬럼이 새 값으로 갱신될 때 수행할 작업을 지정해요. 행이 갱신되지만 참조 컬럼이 실제로 바뀌지 않으면 작업은 없어요. 참조 작업은 제약이 지연되더라도 데이터를 바꾸는 명령의 일부로 실행돼요. 각 절에 대해 가능한 작업은 다음과 같아요.
NO ACTION
삭제·갱신이 외래 키 제약 위반을 만들면 오류를 내요. 제약이 지연되면, 제약 검사 시점에 참조 행이 여전히 존재하면 오류가 발생해요. 기본 작업이에요.
RESTRICT
삭제·갱신될 행이 참조 테이블의 행과 일치하면 오류를 내요. 이는 작업 후 상태가 외래 키 제약을 위반하지 않더라도 그 작업을 막아요. 특히 참조된 행을 서로 다르지만 같다고 비교되는 값으로 갱신하는 것을 막아요. (하지만 컬럼을 같은 값으로 갱신하는 "no-op" 갱신은 막지 않아요.)
시간 외래 키(temporal foreign key)에서는 이 옵션이 지원되지 않아요.
CASCADE
삭제된 행을 참조하는 행을 삭제하거나, 참조 컬럼의 값을 참조 컬럼의 새 값으로 각각 갱신해요.
시간 외래 키에서는 이 옵션이 지원되지 않아요.
SET NULL [ ( column_name [, ... ] ) ]
모든 참조 컬럼을, 또는 지정된 참조 컬럼의 부분 집합을 null로 설정해요. 컬럼 부분 집합은 ON DELETE 작업에서만 지정할 수 있어요.
시간 외래 키에서는 이 옵션이 지원되지 않아요.
SET DEFAULT [ ( column_name [, ... ] ) ]
모든 참조 컬럼을, 또는 지정된 참조 컬럼의 부분 집합을 기본값으로 설정해요. 컬럼 부분 집합은 ON DELETE 작업에서만 지정할 수 있어요. (기본값이 null이 아니라면 그것과 일치하는 행이 참조 테이블에 있어야 하고, 그렇지 않으면 작업이 실패해요.)
시간 외래 키에서는 이 옵션이 지원되지 않아요.
참조 컬럼이 자주 바뀐다면 참조 컬럼에 인덱스를 추가해서 외래 키 제약과 연결된 참조 작업을 더 효율적으로 수행하는 게 현명할 수 있어요.
DEFERRABLE NOT DEFERRABLE
이 절은 제약을 지연할 수 있는지 제어해요. 지연할 수 없는 제약은 모든 명령 직후에 검사돼요. 지연할 수 있는 제약의 검사는 트랜잭션 끝까지(SET CONSTRAINTS 명령으로) 연기할 수 있어요. 기본값은 NOT DEFERRABLE이에요. 현재 이 절을 받아들이는 제약은 UNIQUE, PRIMARY KEY, EXCLUDE, REFERENCES(외래 키)뿐이에요. NOT NULL과 CHECK 제약은 지연할 수 없어요. 지연 가능한 제약은 ON CONFLICT 절을 포함한 INSERT 문에서 충돌 중재자로 쓸 수 없다는 점을 유의하세요.
INITIALLY IMMEDIATE INITIALLY DEFERRED
제약이 지연 가능하다면, 이 절은 제약을 검사할 기본 시점을 지정해요. 제약이 INITIALLY IMMEDIATE면 각 문장 뒤에 검사돼요. 기본값이에요. 제약이 INITIALLY DEFERRED면 트랜잭션 끝에서만 검사돼요. 제약 검사 시점은 SET CONSTRAINTS 명령으로 바꿀 수 있어요.
ENFORCED NOT ENFORCED
제약이 ENFORCED면 데이터베이스 시스템이 적절한 시점(각 문장 후 또는 트랜잭션 끝)에 제약을 검사해 만족을 보장해요. 기본값이에요. 제약이 NOT ENFORCED면 데이터베이스 시스템은 제약을 검사하지 않아요. 제약이 만족되는지 보장하는 건 애플리케이션 코드의 몫이에요. 다만 데이터베이스 시스템은 결과의 정확성에 영향을 주지 않는 최적화 결정을 위해 데이터가 실제로 제약을 만족한다고 가정할 수도 있어요.
NOT ENFORCED 제약은 런타임에 제약을 실제로 검사하는 게 너무 비쌀 때 문서화 용도로 유용할 수 있어요.
이것은 현재 외래 키와 CHECK 제약에서만 지원돼요.
USING method
이 선택 절은 새 테이블의 내용을 저장하는 데 사용할 테이블 접근 방식을 지정해요. 그 방식은 TABLE 타입의 접근 방식이어야 해요. 자세한 내용은 62장을 참고하세요. 이 옵션을 지정하지 않으면 새 테이블에 기본 테이블 접근 방식이 선택돼요. default_table_access_method를 참고하세요.
파티션을 만들 때, 테이블 접근 방식은 설정된 경우 그 파티션 테이블의 접근 방식이에요.
WITH ( storage_parameter [= value] [, ... ] )
이 절은 테이블 또는 인덱스의 선택적 저장 매개변수를 지정해요. 자세한 내용은 아래 Storage Parameters를 참고하세요. 역호환성을 위해 테이블의 WITH 절은 OIDS=FALSE도 포함할 수 있는데, 새 테이블의 행이 OID(객체 식별자)를 포함하지 않도록 지정해요. OIDS=TRUE는 더 이상 지원되지 않아요.
WITHOUT OIDS
이것은 테이블을 WITHOUT OIDS로 선언하는 역호환 문법이에요. WITH OIDS로 테이블을 만드는 것은 더 이상 지원되지 않아요.
ON COMMIT
임시 테이블이 트랜잭션 블록 끝에서 어떻게 동작할지는 ON COMMIT으로 제어해요. 세 가지 옵션이 있어요.
PRESERVE ROWS
트랜잭션이 끝날 때 특별한 작업을 하지 않아요. 기본 동작이에요.
DELETE ROWS
각 트랜잭션 블록이 끝날 때 임시 테이블의 모든 행이 삭제돼요. 본질적으로 각 커밋에서 자동 TRUNCATE가 일어나요. 파티션 테이블에 쓰면 그 파티션에는 계단식으로 전파되지 않아요.
DROP
임시 테이블이 현재 트랜잭션 블록이 끝날 때 삭제돼요. 파티션 테이블에 쓰면 이 작업이 그 파티션을 삭제하고, 상속 자식이 있는 테이블에 쓰면 의존하는 자식들을 삭제해요.
TABLESPACE tablespace_name
tablespace_name은 새 테이블을 만들 테이블스페이스의 이름이에요. 지정하지 않으면 default_tablespace를 참고하고, 테이블이 임시면 temp_tablespaces를 참고해요. 파티션 테이블은 테이블 자체에 저장 공간이 필요 없으므로, 지정된 테이블스페이스는 다른 테이블스페이스가 명시되지 않았을 때 새로 만들어지는 파티션에 쓰일 기본 테이블스페이스로 default_tablespace를 덮어써요.
USING INDEX TABLESPACE tablespace_name
이 절은 UNIQUE, PRIMARY KEY, EXCLUDE 제약과 연결된 인덱스를 만들 테이블스페이스를 선택할 수 있게 해 줘요. 지정하지 않으면 default_tablespace를 참고하고, 테이블이 임시면 temp_tablespaces를 참고해요.
Storage Parameters
WITH 절은 테이블, 그리고 UNIQUE, PRIMARY KEY, EXCLUDE 제약과 연결된 인덱스에 대한 저장 매개변수를 지정할 수 있어요. 인덱스의 저장 매개변수는 CREATE INDEX에 문서화되어 있어요. 테이블에 현재 사용 가능한 저장 매개변수는 아래와 같아요. 이 매개변수들 중 상당수는 보이듯이, 같은 이름에 toast. 접두사가 붙은 추가 매개변수가 있어서 테이블의 2차 TOAST 테이블 동작(TOAST에 대한 자세한 내용은 66.2절 참고)을 제어해요. 테이블 매개변수 값을 설정했는데 같은 toast. 매개변수를 설정하지 않으면 TOAST 테이블이 테이블의 매개변수 값을 사용해요. 파티션 테이블에 이 매개변수들을 지정하는 것은 지원되지 않지만, 개별 리프 파티션에는 지정할 수 있어요.
fillfactor (integer)
테이블의 fillfactor는 10에서 100 사이의 백분율이에요. 100(완전 채움)이 기본값이에요. 더 작은 fillfactor를 지정하면 INSERT 작업이 테이블 페이지를 지정한 백분율까지만 채우고, 각 페이지의 남은 공간은 그 페이지의 행을 갱신하는 데 예약돼요. 이는 UPDATE가 행의 갱신 복사본을 원본과 같은 페이지에 놓을 기회를 주는데, 다른 페이지에 놓는 것보다 효율적이고 heap-only 튜플 갱신이 더 일어나게 해요. 항목이 절대 갱신되지 않는 테이블에는 완전 채움이 최선이지만, 갱신이 잦은 테이블에서는 더 작은 fillfactor가 적합해요. 이 매개변수는 TOAST 테이블에 설정할 수 없어요.
toast_tuple_target (integer)
toast_tuple_target은 긴 컬럼 값을 TOAST 테이블로 압축·이동하려고 시도하기 전 필요한 최소 튜플 길이를 지정하고, 토스팅이 시작된 후 길이를 그 아래로 줄이려는 목표 길이이기도 해요. 이는 External(이동용)·Main(압축용)·Extended(둘 다)로 표시된 컬럼에 영향을 주고 새 튜플에만 적용돼요. 기존 행에는 효과가 없어요. 기본적으로 이 매개변수는 블록당 최소 4개 튜플을 허용하도록 설정되어 있는데, 기본 블록 크기에서 2040바이트가 돼요. 유효한 값은 128바이트와 (블록 크기 - 헤더) 사이인데, 기본적으로 8160바이트예요. 이 값을 바꾸는 것은 아주 짧거나 아주 긴 행에는 유용하지 않을 수 있어요. 기본 설정이 종종 최적에 가깝고, 이 매개변수를 설정하는 게 어떤 경우엔 부정적 효과를 줄 수도 있다는 점을 유의하세요. 이 매개변수는 TOAST 테이블에 설정할 수 없어요.
parallel_workers (integer)
이 테이블의 병렬 스캔을 돕는 데 사용할 워커 수를 설정해요. 설정하지 않으면 시스템이 릴레이션 크기를 기준으로 값을 정해요. 플래너나 병렬 스캔을 쓰는 유틸리티 문이 선택한 실제 워커 수는, 예를 들어 max_worker_processes 설정 때문에 더 적을 수 있어요.
autovacuum_enabled, toast.autovacuum_enabled (boolean)
특정 테이블에 대해 autovacuum 데몬을 켜거나 꺼요. true면 autovacuum 데몬이 24.1.6절에서 논의된 규칙에 따라 이 테이블에 자동 VACUUM·ANALYZE 작업을 수행해요. false면 트랜잭션 ID 랩어라운드 방지를 제외하고는 이 테이블을 autovacuum하지 않아요. 랩어라운드 방지에 대한 자세한 내용은 24.1.5절을 참고하세요. autovacuum 매개변수가 false면 autovacuum 데몬은 (트랜잭션 ID 랩어라운드 방지를 제외하고는) 전혀 실행되지 않는다는 점을 유의하세요. 개별 테이블의 저장 매개변수를 설정해도 그걸 덮어쓰지 않아요. 그래서 이 저장 매개변수를 true로 명시적으로 설정할 일은 거의 없고, false로만 설정해요.
vacuum_index_cleanup, toast.vacuum_index_cleanup (enum)
이 테이블에서 VACUUM을 실행할 때 인덱스 정리를 강제하거나 비활성화해요. 기본값은 AUTO예요. OFF면 인덱스 정리가 비활성화되고, ON이면 활성화되며, AUTO면 VACUUM이 실행될 때마다 동적으로 결정돼요. 동적 동작은 VACUUM이 아주 적은 수의 죽은 튜플을 제거하기 위해 인덱스를 불필요하게 스캔하는 것을 피할 수 있게 해 줘요. 모든 인덱스 정리를 강제로 비활성화하면 VACUUM이 크게 빨라질 수 있지만, 테이블 수정이 잦으면 인덱스가 심하게 부풀어 오를 수도 있어요. VACUUM의 INDEX_CLEANUP 매개변수는, 지정하면 이 옵션의 값을 덮어써요.
vacuum_truncate, toast.vacuum_truncate (boolean)
vacuum_truncate 매개변수의 테이블별 값이에요. VACUUM의 TRUNCATE 매개변수는, 지정하면 이 옵션의 값을 덮어써요.
autovacuum_vacuum_threshold, toast.autovacuum_vacuum_threshold (integer)
autovacuum_vacuum_threshold 매개변수의 테이블별 값이에요.
autovacuum_vacuum_max_threshold, toast.autovacuum_vacuum_max_threshold (integer)
autovacuum_vacuum_max_threshold 매개변수의 테이블별 값이에요.
autovacuum_vacuum_scale_factor, toast.autovacuum_vacuum_scale_factor (floating point)
autovacuum_vacuum_scale_factor 매개변수의 테이블별 값이에요.
autovacuum_vacuum_insert_threshold, toast.autovacuum_vacuum_insert_threshold (integer)
autovacuum_vacuum_insert_threshold 매개변수의 테이블별 값이에요. 특수 값 -1을 쓰면 테이블의 삽입 vacuum을 비활성화할 수 있어요.
autovacuum_vacuum_insert_scale_factor, toast.autovacuum_vacuum_insert_scale_factor (floating point)
autovacuum_vacuum_insert_scale_factor 매개변수의 테이블별 값이에요.
autovacuum_analyze_threshold (integer)
autovacuum_analyze_threshold 매개변수의 테이블별 값이에요.
autovacuum_analyze_scale_factor (floating point)
autovacuum_analyze_scale_factor 매개변수의 테이블별 값이에요.
autovacuum_vacuum_cost_delay, toast.autovacuum_vacuum_cost_delay (floating point)
autovacuum_vacuum_cost_delay 매개변수의 테이블별 값이에요.
autovacuum_vacuum_cost_limit, toast.autovacuum_vacuum_cost_limit (integer)
autovacuum_vacuum_cost_limit 매개변수의 테이블별 값이에요.
autovacuum_freeze_min_age, toast.autovacuum_freeze_min_age (integer)
vacuum_freeze_min_age 매개변수의 테이블별 값이에요. autovacuum은 시스템 전역 autovacuum_freeze_max_age 설정의 절반보다 큰 테이블별 autovacuum_freeze_min_age 매개변수는 무시한다는 점을 유의하세요.
autovacuum_freeze_max_age, toast.autovacuum_freeze_max_age (integer)
autovacuum_freeze_max_age 매개변수의 테이블별 값이에요. autovacuum은 시스템 전역 설정보다 큰 테이블별 autovacuum_freeze_max_age 매개변수는 무시한다는 점을 유의하세요(더 작게만 설정할 수 있어요).
autovacuum_freeze_table_age, toast.autovacuum_freeze_table_age (integer)
vacuum_freeze_table_age 매개변수의 테이블별 값이에요.
autovacuum_multixact_freeze_min_age, toast.autovacuum_multixact_freeze_min_age (integer)
vacuum_multixact_freeze_min_age 매개변수의 테이블별 값이에요. autovacuum은 시스템 전역 autovacuum_multixact_freeze_max_age 설정의 절반보다 큰 테이블별 autovacuum_multixact_freeze_min_age 매개변수는 무시한다는 점을 유의하세요.
autovacuum_multixact_freeze_max_age, toast.autovacuum_multixact_freeze_max_age (integer)
autovacuum_multixact_freeze_max_age 매개변수의 테이블별 값이에요. autovacuum은 시스템 전역 설정보다 큰 테이블별 autovacuum_multixact_freeze_max_age 매개변수는 무시한다는 점을 유의하세요(더 작게만 설정할 수 있어요).
autovacuum_multixact_freeze_table_age, toast.autovacuum_multixact_freeze_table_age (integer)
vacuum_multixact_freeze_table_age 매개변수의 테이블별 값이에요.
log_autovacuum_min_duration, toast.log_autovacuum_min_duration (integer)
log_autovacuum_min_duration 매개변수의 테이블별 값이에요.
vacuum_max_eager_freeze_failure_rate, toast.vacuum_max_eager_freeze_failure_rate (floating point)
vacuum_max_eager_freeze_failure_rate 매개변수의 테이블별 값이에요.
user_catalog_table (boolean)
논리 복제를 위해 테이블을 추가 카탈로그 테이블로 선언해요. 자세한 내용은 47.6.2절을 참고하세요. 이 매개변수는 TOAST 테이블에 설정할 수 없어요.
Notes
PostgreSQL은 각 unique 제약 조건과 primary key 제약 조건에 대해 유일성을 강제하는 인덱스를 자동으로 만들어요. 그래서 primary key 컬럼에 인덱스를 명시적으로 만들 필요가 없어요. (더 자세한 내용은 CREATE INDEX 참고.)
unique 제약 조건과 primary key는 현재 구현에서 상속되지 않아요. 이 때문에 상속과 unique 제약의 조합은 꽤 제 기능을 못 해요.
테이블은 1600개보다 많은 컬럼을 가질 수 없어요. (실제로는 튜플 길이 제약 때문에 유효 한도가 보통 더 낮아요.)
Examples
테이블 films와 테이블 distributors를 만들어 볼게요.
CREATE TABLE films (
code char(5) CONSTRAINT firstkey PRIMARY KEY,
title varchar(40) NOT NULL,
did integer NOT NULL,
date_prod date,
kind varchar(10),
len interval hour to minute
);
CREATE TABLE distributors (
did integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
name varchar(40) NOT NULL CHECK (name <> '')
);
2차원 배열을 가진 테이블을 만들어 볼게요.
CREATE TABLE array_int (
vector int[][]
);
테이블 films에 대한 unique 테이블 제약 조건을 정의해 볼게요. unique 테이블 제약 조건은 테이블의 한 개 이상의 컬럼에 정의할 수 있어요.
CREATE TABLE films (
code char(5),
title varchar(40),
did integer,
date_prod date,
kind varchar(10),
len interval hour to minute,
CONSTRAINT production UNIQUE(date_prod)
);
체크 컬럼 제약 조건을 정의해 볼게요.
CREATE TABLE distributors (
did integer CHECK (did > 100),
name varchar(40)
);
체크 테이블 제약 조건을 정의해 볼게요.
CREATE TABLE distributors (
did integer,
name varchar(40),
CONSTRAINT con1 CHECK (did > 100 AND name <> '')
);
테이블 films에 대한 primary key 테이블 제약 조건을 정의해 볼게요.
CREATE TABLE films (
code char(5),
title varchar(40),
did integer,
date_prod date,
kind varchar(10),
len interval hour to minute,
CONSTRAINT code_title PRIMARY KEY(code,title)
);
테이블 distributors에 대한 primary key 제약 조건을 정의해 볼게요. 다음 두 예시는 동등해요. 첫 번째는 테이블 제약 조건 문법을, 두 번째는 컬럼 제약 조건 문법을 쓴 거예요.
CREATE TABLE distributors (
did integer,
name varchar(40),
PRIMARY KEY(did)
);
CREATE TABLE distributors (
did integer PRIMARY KEY,
name varchar(40)
);
컬럼 name에 리터럴 상수 기본값을 지정하고, 컬럼 did의 기본값이 시퀀스 객체의 다음 값을 선택해서 생성되게 하고, modtime의 기본값이 행이 삽입되는 시점이 되게 해 볼게요.
CREATE TABLE distributors (
name varchar(40) DEFAULT 'Luso Films',
did integer DEFAULT nextval('distributors_serial'),
modtime timestamp DEFAULT current_timestamp
);
테이블 distributors에 NOT NULL 컬럼 제약 조건 두 개를 정의해 볼게요. 그중 하나는 이름을 명시적으로 주었어요.
CREATE TABLE distributors (
did integer CONSTRAINT no_null NOT NULL,
name varchar(40) NOT NULL
);
name 컬럼에 대한 unique 제약 조건을 정의해 볼게요.
CREATE TABLE distributors (
did integer,
name varchar(40) UNIQUE
);
같은 것을 테이블 제약 조건으로 지정한 거예요.
CREATE TABLE distributors (
did integer,
name varchar(40),
UNIQUE(name)
);
같은 테이블을 만들되, 테이블과 unique 인덱스 모두에 70% fill factor를 지정해 볼게요.
CREATE TABLE distributors (
did integer,
name varchar(40),
UNIQUE(name) WITH (fillfactor=70)
)
WITH (fillfactor=70);
두 원이 겹치는 것을 막는 배제 제약 조건을 가진 테이블 circles를 만들어 볼게요.
CREATE TABLE circles (
c circle,
EXCLUDE USING gist (c WITH &&)
);
테이블스페이스 diskvol1에 테이블 cinemas를 만들어 볼게요.
CREATE TABLE cinemas (
id serial,
name text,
location text
) TABLESPACE diskvol1;
복합 타입과 형식 테이블을 만들어 볼게요.
CREATE TYPE employee_type AS (name text, salary numeric);
CREATE TABLE employees OF employee_type (
PRIMARY KEY (name),
salary WITH OPTIONS DEFAULT 1000
);
범위 파티션 테이블을 만들어 볼게요.
CREATE TABLE measurement (
logdate date not null,
peaktemp int,
unitsales int
) PARTITION BY RANGE (logdate);
파티션 키에 여러 컬럼이 있는 범위 파티션 테이블을 만들어 볼게요.
CREATE TABLE measurement_year_month (
logdate date not null,
peaktemp int,
unitsales int
) PARTITION BY RANGE (EXTRACT(YEAR FROM logdate), EXTRACT(MONTH FROM logdate));
리스트 파티션 테이블을 만들어 볼게요.
CREATE TABLE cities (
city_id bigserial not null,
name text not null,
population bigint
) PARTITION BY LIST (left(lower(name), 1));
해시 파티션 테이블을 만들어 볼게요.
CREATE TABLE orders (
order_id bigint not null,
cust_id bigint not null,
status text
) PARTITION BY HASH (order_id);
범위 파티션 테이블의 파티션을 만들어 볼게요.
CREATE TABLE measurement_y2016m07
PARTITION OF measurement (
unitsales DEFAULT 0
) FOR VALUES FROM ('2016-07-01') TO ('2016-08-01');
파티션 키에 여러 컬럼이 있는 범위 파티션 테이블의 파티션 몇 개를 만들어 볼게요.
CREATE TABLE measurement_ym_older
PARTITION OF measurement_year_month
FOR VALUES FROM (MINVALUE, MINVALUE) TO (2016, 11);
CREATE TABLE measurement_ym_y2016m11
PARTITION OF measurement_year_month
FOR VALUES FROM (2016, 11) TO (2016, 12);
CREATE TABLE measurement_ym_y2016m12
PARTITION OF measurement_year_month
FOR VALUES FROM (2016, 12) TO (2017, 01);
CREATE TABLE measurement_ym_y2017m01
PARTITION OF measurement_year_month
FOR VALUES FROM (2017, 01) TO (2017, 02);
리스트 파티션 테이블의 파티션을 만들어 볼게요.
CREATE TABLE cities_ab
PARTITION OF cities (
CONSTRAINT city_id_nonzero CHECK (city_id != 0)
) FOR VALUES IN ('a', 'b');
그 자체가 더 파티셔닝된 리스트 파티션 테이블의 파티션을 만들고, 거기에 파티션을 추가해 볼게요.
CREATE TABLE cities_ab
PARTITION OF cities (
CONSTRAINT city_id_nonzero CHECK (city_id != 0)
) FOR VALUES IN ('a', 'b') PARTITION BY RANGE (population);
CREATE TABLE cities_ab_10000_to_100000
PARTITION OF cities_ab FOR VALUES FROM (10000) TO (100000);
해시 파티션 테이블의 파티션들을 만들어 볼게요.
CREATE TABLE orders_p1 PARTITION OF orders
FOR VALUES WITH (MODULUS 4, REMAINDER 0);
CREATE TABLE orders_p2 PARTITION OF orders
FOR VALUES WITH (MODULUS 4, REMAINDER 1);
CREATE TABLE orders_p3 PARTITION OF orders
FOR VALUES WITH (MODULUS 4, REMAINDER 2);
CREATE TABLE orders_p4 PARTITION OF orders
FOR VALUES WITH (MODULUS 4, REMAINDER 3);
기본 파티션을 만들어 볼게요.
CREATE TABLE cities_partdef
PARTITION OF cities DEFAULT;
Compatibility
CREATE TABLE 명령은 아래 나열된 예외를 제외하고 SQL 표준을 따르는 편이에요.
Temporary Tables
CREATE TEMPORARY TABLE 문법은 SQL 표준과 비슷하지만 그 효과는 같지 않아요. 표준에서는 임시 테이블이 한 번 정의되면 그것이 필요한 모든 세션에 (빈 내용으로) 자동으로 존재해요. 반면 PostgreSQL은 사용할 각 임시 테이블마다 각 세션이 자신의 CREATE TEMPORARY TABLE 명령을 내려야 해요. 이 덕분에 서로 다른 세션이 같은 임시 테이블 이름을 다른 목적으로 쓸 수 있어요. 표준의 방식은 주어진 임시 테이블 이름의 모든 인스턴스가 같은 테이블 구조를 가지도록 제약하죠.
표준의 임시 테이블 동작 정의는 널리 무시돼요. PostgreSQL의 이 부분에 대한 동작은 여러 다른 SQL 데이터베이스와 비슷해요.
SQL 표준은 또한 전역·지역 임시 테이블을 구분하는데, 지역 임시 테이블은 세션 안의 각 SQL 모듈에 대해 별도의 내용 집합을 가지지만 정의는 여전히 세션 간에 공유돼요. PostgreSQL은 SQL 모듈을 지원하지 않으므로 이 구분은 PostgreSQL에서 관련이 없어요.
호환성을 위해 PostgreSQL은 임시 테이블 선언에서 GLOBAL·LOCAL 키워드를 받아들이지만, 현재 아무 효과가 없어요. 이런 키워드의 사용은 권장하지 않아요. 향후 PostgreSQL 버전이 더 표준에 부합하는 해석을 채택할지도 모르기 때문이죠.
임시 테이블의 ON COMMIT 절도 SQL 표준과 비슷하지만 몇 가지 차이가 있어요. ON COMMIT 절을 생략하면 SQL은 기본 동작이 ON COMMIT DELETE ROWS라고 지정해요. 하지만 PostgreSQL의 기본 동작은 ON COMMIT PRESERVE ROWS예요. ON COMMIT DROP 옵션은 SQL에 존재하지 않아요.
Non-Deferred Uniqueness Constraints
UNIQUE나 PRIMARY KEY 제약이 지연 가능하지 않으면, PostgreSQL은 행이 삽입·수정될 때마다 즉시 유일성을 검사해요. SQL 표준은 유일성이 문장 끝에서만 강제되어야 한다고 말해요. 예를 들어 단일 명령이 여러 키 값을 갱신할 때 이 차이가 영향을 줘요. 표준에 부합하는 동작을 얻으려면 제약을 DEFERRABLE로 선언하되 지연하지 않게(즉 INITIALLY IMMEDIATE) 선언해요. 이러면 즉시 유일성 검사보다 상당히 느릴 수 있다는 점을 알아 두세요.
Column Check Constraints
SQL 표준은 CHECK 컬럼 제약 조건이 적용되는 컬럼만 참조할 수 있고, 여러 컬럼을 참조할 수 있는 건 CHECK 테이블 제약 조건뿐이라고 말해요. PostgreSQL은 이 제한을 강제하지 않아요. 컬럼·테이블 체크 제약을 동일하게 취급하죠.
EXCLUDE Constraint
EXCLUDE 제약 조건 타입은 PostgreSQL 확장 기능이에요.
Foreign Key Constraints
외래 키 작업 SET DEFAULT·SET NULL에서 컬럼 목록을 지정할 수 있는 능력은 PostgreSQL 확장 기능이에요.
외래 키 제약 조건이 primary key·unique 제약 조건의 컬럼 대신 unique 인덱스의 컬럼을 참조할 수 있는 것도 PostgreSQL 확장 기능이에요.
NULL “Constraint”
NULL "제약 조건"(실제로는 제약이 아닌 것)은 SQL 표준에 대한 PostgreSQL 확장으로, 다른 몇몇 데이터베이스 시스템과의 호환성(그리고 NOT NULL 제약과의 대칭성)을 위해 포함됐어요. 어떤 컬럼의 기본값이므로 그 존재는 그저 잡음일 뿐이에요.
Constraint Naming
SQL 표준은 테이블·도메인 제약 조건의 이름이 그 테이블·도메인을 포함하는 스키마 전체에 걸쳐 고유해야 한다고 말해요. PostgreSQL은 더 관대해서, 제약 이름이 특정 테이블·도메인에 붙은 제약들 사이에서만 고유하면 돼요. 다만 이 여분의 자유는 인덱스 기반 제약(UNIQUE, PRIMARY KEY, EXCLUDE 제약)에는 존재하지 않아요. 연결된 인덱스가 제약과 같은 이름을 갖고, 인덱스 이름은 같은 스키마 안의 모든 릴레이션에 걸쳐 고유해야 하기 때문이에요.
Inheritance
INHERITS 절을 통한 다중 상속은 PostgreSQL 언어 확장이에요. SQL:1999 이후는 다른 문법과 다른 의미로 단일 상속을 정의해요. SQL:1999 스타일 상속은 PostgreSQL이 아직 지원하지 않아요.
Zero-Column Tables
PostgreSQL은 컬럼이 없는 테이블을 만들 수 있게 해 줘요(예: CREATE TABLE foo();). 이는 컬럼 없는 테이블을 허용하지 않는 SQL 표준의 확장이에요. 컬럼 없는 테이블 자체는 그리 유용하지 않지만, 허용하지 않으면 ALTER TABLE DROP COLUMN에 이상한 특수 경우가 생기므로 이 사양 제한을 무시하는 게 더 깔끔해요.
Multiple Identity Columns
PostgreSQL은 테이블이 하나보다 많은 identity 컬럼을 가질 수 있게 해 줘요. 표준은 테이블이 최대 한 개의 identity 컬럼을 가질 수 있다고 지정해요. 이 완화는 주로 스키마 변경·마이그레이션에 더 많은 유연성을 주기 위한 거예요. INSERT 명령은 전체 문장에 적용되는 override 절을 하나만 지원하므로, 서로 다른 동작을 가진 identity 컬럼이 여러 개 있으면 잘 지원되지 않는다는 점을 유의하세요.
Generated Columns
STORED·VIRTUAL 옵션은 표준이 아니지만 다른 SQL 구현에서도 사용돼요. SQL 표준은 생성 컬럼의 저장을 지정하지 않아요.
LIKE Clause
SQL 표준에 LIKE 절이 있지만, PostgreSQL이 받아들이는 옵션 중 많은 것이 표준에 없고, 표준의 일부 옵션은 PostgreSQL이 구현하지 않아요.
WITH Clause
WITH 절은 PostgreSQL 확장 기능이고, 저장 매개변수는 표준에 없어요.
Tablespaces
PostgreSQL의 테이블스페이스 개념은 표준의 일부가 아니에요. 따라서 TABLESPACE와 USING INDEX TABLESPACE 절은 확장 기능이에요.
Typed Tables
형식 테이블은 SQL 표준의 부분 집합을 구현해요. 표준에 따르면 형식 테이블은 기본 복합 타입에 대응하는 컬럼에 더해 "자기 참조 컬럼(self-referencing column)"이라는 다른 컬럼 하나를 가져요. PostgreSQL은 자기 참조 컬럼을 명시적으로 지원하지 않아요.
PARTITION BY Clause
PARTITION BY 절은 PostgreSQL 확장 기능이에요.
PARTITION OF Clause
PARTITION OF 절은 PostgreSQL 확장 기능이에요.
더 알아보기 (Learn more)
ALTER TABLE— 테이블의 정의·속성을 바꿀 때 써요.DROP TABLE— 테이블을 삭제할 때 써요.CREATE INDEX— 테이블에 인덱스를 만들 때 써요.- Table Partitioning — 파티셔닝 전략을 다루는 5.12절이에요.
- CREATE TYPE — 형식 테이블의 기반이 되는 복합 타입을 만들 때 써요.