INSERT
INSERT
테이블에 새 행을 만드는 명령이에요. 값 표현식으로 지정한 행을 하나 이상 넣거나, 쿼리 결과로 나온 행을 0개 이상 넣을 수 있어요. ON CONFLICT로 충돌 시 대체 동작을 지정해 UPSERT 방식으로도 쓸 수 있고, RETURNING 절로 삽입(또는 갱신)된 행의 값을 되돌려받을 수 있어요.
출처: PostgreSQL 문서
본문
개요 (Synopsis)
[ WITH [ RECURSIVE ] with_query [, ...] ]
INSERT INTO table_name [ AS alias ] [ ( column_name [, ...] ) ]
[ OVERRIDING { SYSTEM | USER } VALUE ]
{ DEFAULT VALUES | VALUES ( { expression | DEFAULT } [, ...] ) [, ...] | query }
[ ON CONFLICT [ conflict_target ] conflict_action ]
[ RETURNING [ WITH ( { OLD | NEW } AS output_alias [, ...] ) ]
{ * | output_expression [ [ AS ] output_name ] } [, ...] ]
where conflict_target can be one of:
( { index_column_name | ( index_expression ) } [ COLLATE collation ] [ opclass ] [, ...] ) [ WHERE index_predicate ]
ON CONSTRAINT constraint_name
and conflict_action is one of:
DO NOTHING
DO UPDATE SET { column_name = { expression | DEFAULT } |
( column_name [, ...] ) = [ ROW ] ( { expression | DEFAULT } [, ...] ) |
( column_name [, ...] ) = ( sub-SELECT )
} [, ...]
[ WHERE condition ]
설명 (Description)
INSERT는 테이블에 새 행을 삽입해요. 값 표현식으로 지정된 행을 하나 이상 삽입하거나, 쿼리에서 나온 행을 0개 이상 삽입할 수 있어요.
대상 열 이름은 어떤 순서로든 나열할 수 있어요. 열 이름 목록이 전혀 주어지지 않으면 기본값은 선언된 순서대로 테이블의 모든 열이에요. 또는 VALUES 절이나 query가 공급하는 열이 N개뿐이라면 처음 N개의 열 이름이 기본값이 돼요. VALUES 절이나 query가 공급하는 값은 명시적 또는 암시적 열 목록과 왼쪽에서 오른쪽으로 연결돼요.
명시적 또는 암시적 열 목록에 없는 각 열은 기본값으로 채워지는데, 그 열의 선언된 기본값 또는 기본값이 없으면 null로 채워져요.
어떤 열의 표현식이 올바른 데이터 타입이 아니면 자동 타입 변환이 시도돼요.
고유 인덱스가 없는 테이블에 대한 INSERT는 동시 활동에 차단되지 않아요. 고유 인덱스가 있는 테이블은 동시 세션이 삽입되는 고유 인덱스 값과 일치하는 행을 잠그거나 수정하는 작업을 수행하면 차단될 수 있는데, 자세한 내용은 63.5절에서 다뤄져요. ON CONFLICT는 고유 제약 또는 배타 제약 위반 오류를 발생시키는 대신 대체 동작을 지정하는 데 사용할 수 있어요. (아래 ON CONFLICT 절을 참고하세요.)
선택적인 RETURNING 절은 INSERT가 실제로 삽입(또는 ON CONFLICT DO UPDATE 절을 사용했다면 갱신)된 각 행을 기반으로 값을 계산해 반환하도록 해요. 이것은 주로 시퀀스 일련번호처럼 기본값으로 공급된 값을 얻는 데 유용해요. 하지만 테이블의 열을 사용하는 어떤 표현식도 허용돼요. RETURNING 목록의 문법은 SELECT의 출력 목록과 동일해요. 성공적으로 삽입되거나 갱신된 행만 반환돼요. 예를 들어 ON CONFLICT DO UPDATE ... WHERE 절 condition이 충족되지 않아 행이 잠겼지만 갱신되지는 않았다면 그 행은 반환되지 않아요.
테이블에 삽입하려면 그 테이블에 대한 INSERT 권한이 있어야 해요. ON CONFLICT DO UPDATE가 있으면 테이블에 대한 UPDATE 권한도 필요해요.
열 목록이 지정되면 나열된 열에 대해서만 INSERT 권한이 있으면 돼요. 마찬가지로 ON CONFLICT DO UPDATE가 지정되면 갱신하도록 나열된 열에 대해서만 UPDATE 권한이 있으면 돼요. 그러나 모든 형태의 ON CONFLICT는 값이 읽히는 모든 열에 대한 SELECT 권한도 요구해요. 여기에는 conflict_target에 언급된 열(중재(arbiter) 제약이 참조하는 열 포함)과 ON CONFLICT DO UPDATE expression이나 WHERE 절 condition에 언급된 모든 열이 포함돼요.
RETURNING 절의 사용은 RETURNING에 언급된 모든 열에 대한 SELECT 권한을 요구해요. query 절을 사용해 쿼리에서 행을 삽입한다면, 당연히 쿼리에 사용된 모든 테이블이나 열에 대한 SELECT 권한이 있어야 해요.
파라미터 (Parameters)
삽입 (Inserting)
이 절은 새 행을 삽입할 때만 사용할 수 있는 파라미터를 다뤄요. ON CONFLICT 절과 함께만 사용하는 파라미터는 별도로 설명돼요.
*with_query* — WITH 절은 INSERT 쿼리 안에서 이름으로 참조할 수 있는 하나 이상의 서브쿼리를 지정할 수 있게 해요. 자세한 내용은 7.8절과 SELECT를 참고하세요.
query(SELECT 명령문) 자체에도 WITH 절이 포함될 수 있어요. 그런 경우 두 with_query 집합 모두 query 안에서 참조될 수 있지만, 두 번째 것이 더 밀접하게 중첩되어 있으므로 우선권을 가져요.
*table_name* — 기존 테이블의 이름이에요(선택적으로 스키마 한정).
*alias* — table_name의 대체 이름이에요. 별칭이 제공되면 테이블의 실제 이름을 완전히 숨겨요. ON CONFLICT DO UPDATE가 excluded라는 이름의 테이블을 대상으로 할 때 특히 유용한데, 그렇지 않으면 excluded가 삽입을 위해 제안된 행을 나타내는 특수 테이블의 이름으로 해석되기 때문이에요.
*column_name* — table_name이 이름을 나타내는 테이블의 열 이름이에요. 열 이름은 필요할 때 하위 필드 이름이나 배열 첨자로 한정될 수 있어요. (복합 열의 일부 필드에만 삽입하면 나머지 필드는 null로 남아요.) ON CONFLICT DO UPDATE로 열을 참조할 때는 대상 열의 지정에 테이블 이름을 포함하지 마세요. 예를 들어 INSERT INTO table_name ... ON CONFLICT DO UPDATE SET table_name.col = 1은 유효하지 않아요(이것은 UPDATE의 일반적인 동작을 따릅니다).
OVERRIDING SYSTEM VALUE — 이 절이 지정되면 identity 열에 대해 공급된 모든 값이 기본 시퀀스 생성 값을 재정의해요.
GENERATED ALWAYS로 정의된 identity 열의 경우, OVERRIDING SYSTEM VALUE나 OVERRIDING USER VALUE를 지정하지 않고 명시적 값(DEFAULT 제외)을 삽입하는 것은 오류예요. (GENERATED BY DEFAULT로 정의된 identity 열의 경우 OVERRIDING SYSTEM VALUE는 정상 동작이며 지정해도 아무것도 하지 않지만, PostgreSQL은 확장으로서 이를 허용해요.)
OVERRIDING USER VALUE — 이 절이 지정되면 identity 열에 대해 공급된 모든 값이 무시되고 기본 시퀀스 생성 값이 적용돼요.
이 절은 예를 들어 테이블 사이에서 값을 복사할 때 유용해요. INSERT INTO tbl2 OVERRIDING USER VALUE SELECT * FROM tbl1을 쓰면 tbl1에서 tbl2에 identity 열이 아닌 모든 열을 복사하면서, tbl2의 identity 열 값은 tbl2와 연관된 시퀀스가 생성해요.
DEFAULT VALUES — 모든 열이 기본값으로 채워지는데, 각 열에 DEFAULT가 명시적으로 지정된 것처럼요. (이 형태에서는 OVERRIDING 절이 허용되지 않아요.)
*expression* — 해당 열에 할당할 표현식 또는 값이에요.
DEFAULT — 해당 열이 기본값으로 채워져요. identity 열은 연관된 시퀀스가 생성한 새 값으로 채워져요. 생성된(column) 열에 대해 이것을 지정하는 것은 허용되지만, 단지 생성 표현식에서 열을 계산하는 정상 동작을 지정할 뿐이에요.
*query* — 삽입할 행을 공급하는 쿼리(SELECT 명령문)예요. 문법 설명은 SELECT 명령문을 참고하세요.
*output_alias* — RETURNING 목록에서 OLD 또는 NEW 행에 대한 선택적인 대체 이름이에요.
기본적으로 대상 테이블의 이전 값은 OLD.column_name 또는 OLD.*를 써서 반환할 수 있고, 새 값은 NEW.column_name 또는 NEW.*를 써서 반환할 수 있어요. 별칭이 제공되면 이 이름들은 숨겨지고 이전 또는 새 행은 별칭을 사용해 참조해야 해요. 예를 들어 RETURNING WITH (OLD AS o, NEW AS n) o.*, n.*와 같아요.
*output_expression* — 각 행이 삽입되거나 갱신된 후 INSERT 명령이 계산하고 반환하는 표현식이에요. 표현식은 table_name이 이름을 나타내는 테이블의 어떤 열 이름이든 사용할 수 있어요. 삽입되거나 갱신된 행(들)의 모든 열을 반환하려면 *를 쓰세요.
열 이름이나 *는 OLD나 NEW, 또는 OLD나 NEW에 대한 해당 output_alias로 한정되어 이전 또는 새 값을 반환하게 할 수 있어요. 한정되지 않은 열 이름이나 *, 또는 대상 테이블 이름이나 별칭으로 한정된 열 이름이나 *는 새 값을 반환해요.
단순한 INSERT의 경우 모든 이전 값은 NULL이에요. 그러나 ON CONFLICT DO UPDATE 절이 있는 INSERT의 경우 이전 값은 NULL이 아닐 수 있어요.
*output_name* — 반환된 열에 사용할 이름이에요.
ON CONFLICT 절
선택적인 ON CONFLICT 절은 고유 위반 또는 배타 제약 위반 오류를 발생시키는 대신 대체 동작을 지정해요. 삽입을 위해 제안된 각 개별 행에 대해 삽입이 진행되거나, conflict_target이 지정한 중재(arbiter) 제약 또는 인덱스가 위반되면 대체 conflict_action이 수행돼요. ON CONFLICT DO NOTHING은 '아무것도 하지 않음(DO NOTHING)'을 대체 동작으로 삼아 그저 행 삽입을 피해요. ON CONFLICT DO UPDATE는 대체 동작으로서 삽입을 위해 제안된 행과 충돌하는 기존 행을 갱신해요.
conflict_target은 *고유 인덱스 추론(unique index inference)*을 수행할 수 있어요. 추론을 수행할 때 하나 이상의 index_column_name 열 및/또는 index_expression 표현식과 선택적인 index_predicate로 구성돼요. 순서와 무관하게 conflict_target이 지정한 열/표현식을 정확히 포함하는 모든 table_name 고유 인덱스가 중재 인덱스로 추론(선택)돼요. index_predicate가 지정되면 추론을 위한 추가 요구 사항으로서 중재 인덱스를 만족해야 해요. 즉, 다른 모든 기준을 만족하는 인덱스가 있다면 비부분 고유 인덱스(술어가 없는 고유 인덱스)가 추론(따라서 ON CONFLICT에 사용)된다는 뜻이에요. 추론 시도가 성공하지 못하면 오류가 발생해요.
ON CONFLICT DO UPDATE는 원자적인 INSERT 또는 UPDATE 결과를 보장해요. 독립적인 오류가 없다면 두 결과 중 하나가 높은 동시성에서도 보장돼요. 이것은 UPSERT — "UPDATE or INSERT"로도 알려져 있어요.
*conflict_target* — 중재 인덱스를 선택해 ON CONFLICT가 대체 동작을 취할 충돌을 지정해요. 고유 인덱스 추론을 수행하거나 제약을 명시적으로 이름으로 지정해요. ON CONFLICT DO NOTHING의 경우 conflict_target 지정은 선택 사항이에요. 생략하면 사용 가능한 모든 제약(및 고유 인덱스)과의 충돌이 처리돼요. ON CONFLICT DO UPDATE의 경우 conflict_target을 반드시 제공해야 해요.
*conflict_action* — conflict_action은 대체 ON CONFLICT 동작을 지정해요. DO NOTHING이거나, 충돌 시 수행할 UPDATE 동작의 정확한 세부 사항을 지정하는 DO UPDATE 절일 수 있어요. ON CONFLICT DO UPDATE의 SET 및 WHERE 절은 테이블 이름(또는 별칭)으로 기존 행에 접근할 수 있고, 특수 excluded 테이블로 삽입을 위해 제안된 행에 접근할 수 있어요. 해당 excluded 열이 읽히는 대상 테이블의 어떤 열에도 SELECT 권한이 필요해요.
모든 행별 BEFORE INSERT 트리거의 효과는 excluded 값에 반영된다는 점에 유의하세요. 그 효과가 행이 삽입에서 제외되는 데 기여했을 수 있기 때문이에요.
*index_column_name* — table_name 열의 이름이에요. 중재 인덱스를 추론하는 데 사용돼요. CREATE INDEX 형식을 따릅니다. index_column_name에 대한 SELECT 권한이 필요해요.
*index_expression* — index_column_name과 비슷하지만, 인덱스 정의 안에 나타나는 table_name 열의 표현식(단순 열이 아닌)을 추론하는 데 사용돼요. CREATE INDEX 형식을 따릅니다. index_expression 안에 나타나는 어떤 열에 대한 SELECT 권한이 필요해요.
*collation* — 지정되면 추론 중 일치하려면 해당 index_column_name이나 index_expression이 특정 콜레이션(collation)을 사용해야 한다고 강제해요. 일반적으로 콜레이션은 제약 위반이 발생하는지 여부에 영향을 주지 않으므로 생략해요. CREATE INDEX 형식을 따릅니다.
*opclass* — 지정되면 추론 중 일치하려면 해당 index_column_name이나 *index_expression이 특정 연산자 클래스(operator class)를 사용해야 한다고 강제해요. 일반적으로 타입의 연산자 클래스들 간에 *equality* 의미가 종종 동등하거나, 정의된 고유 인덱스가 동등성에 대한 관련 정의를 가진다고 신뢰하면 충분하기 때문에 생략해요. CREATE INDEX` 형식을 따릅니다.
*index_predicate* — 부분 고유 인덱스(partial unique index)의 추론을 허용하는 데 사용돼요. 술어를 만족하는 어떤 인덱스든(실제로 부분 인덱스일 필요는 없어요) 추론될 수 있어요. CREATE INDEX 형식을 따릅니다. index_predicate 안에 나타나는 어떤 열에 대한 SELECT 권한이 필요해요.
*constraint_name* — 제약이나 인덱스를 추론하는 대신 중재 제약을 이름으로 명시적으로 지정해요.
*condition* — boolean 타입의 값을 반환하는 표현식이에요. 이 표현식이 true를 반환하는 행만 갱신되지만, ON CONFLICT DO UPDATE 동작이 수행될 때는 모든 행이 잠겨요. condition은 갱신 후보로 충돌이 식별된 후 마지막에 평가된다는 점에 유의하세요.
ON CONFLICT DO UPDATE와 함께 배타 제약은 중재자로 지원되지 않는다는 점에 유의하세요. 모든 경우에 ON CONFLICT는 중재자로 NOT DEFERRABLE 제약과 고유 인덱스만 지원해요.
ON CONFLICT DO UPDATE 절이 있는 INSERT는 "결정적(deterministic)" 명령문이에요. 즉 명령이 어떤 단일 기존 행에도 한 번 이상 영향을 주는 것이 허용되지 않으며, 이 상황이 발생하면 카디널리티 위반(cardinality violation) 오류가 발생해요. 삽입을 위해 제안된 행은 중재 인덱스나 제약이 제한하는 속성 측면에서 서로 중복되지 않아야 해요.
분할 테이블(partitioned table)에 적용되는 INSERT의 ON CONFLICT DO UPDATE 절이 충돌하는 행의 분할 키를, 그 행을 새 파티션으로 이동해야 할 정도로 갱신하는 것은 현재 지원되지 않는다는 점에 유의하세요.
팁 (Tip)
ON CONFLICT ON CONSTRAINTconstraint_name으로 제약을 직접 이름으로 지정하는 것보다 고유 인덱스 추론을 사용하는 것이 종종 더 선호돼요. 추론은 기본 인덱스가 대체되는 인덱스를 내려놓기 전CREATE UNIQUE INDEX ... CONCURRENTLY를 사용하는 것처럼, 겹치는 방식으로 또 다른 다소 동등한 인덱스로 교체되어도 계속 올바르게 동작해요.
경고 (Warning)
고유 인덱스에서
CREATE INDEX CONCURRENTLY나REINDEX CONCURRENTLY가 실행되는 동안 같은 테이블에 대한INSERT ... ON CONFLICT명령문은 고유 위반으로 예기치 않게 실패할 수 있어요.
출력 (Outputs)
성공적으로 완료되면 INSERT 명령은 다음 형태의 명령 태그를 반환해요.
INSERT oid count
count는 삽입되거나 갱신된 행의 수예요. oid는 항상 0이에요(예전에는 count가 정확히 하나이고 대상 테이블이 WITH OIDS로 선언된 경우 삽입된 행에 할당된 OID였고 그 외에는 0이었지만, WITH OIDS로 테이블을 만드는 것은 더 이상 지원되지 않아요).
INSERT 명령에 RETURNING 절이 포함되어 있으면, 결과는 RETURNING 목록에 정의된 열과 값을 포함하는 SELECT 명령문의 결과와 비슷해요. 명령에 의해 삽입되거나 갱신된 행(들)을 대상으로 계산돼요.
참고 (Notes)
지정된 테이블이 분할 테이블이면 각 행이 적절한 파티션으로 라우팅되어 그 안에 삽입돼요. 지정된 테이블이 파티션이라면 입력 행 중 하나가 파티션 제약을 위반할 경우 오류가 발생해요.
MERGE를 고려해 볼 수도 있는데, 왜냐하면 그것은 단일 명령문 안에서 INSERT, UPDATE, DELETE를 섞는 것을 허용하기 때문이에요. MERGE를 참고하세요.
예제 (Examples)
films 테이블에 단일 행을 삽입하려면:
INSERT INTO films VALUES
('UA502', 'Bananas', 105, '1971-07-13', 'Comedy', '82 minutes');
이 예제에서 len 열이 생략되므로 기본값을 가지게 돼요:
INSERT INTO films (code, title, did, date_prod, kind)
VALUES ('T_601', 'Yojimbo', 106, '1961-06-16', 'Drama');
이 예제는 날짜 열에 값을 지정하는 대신 DEFAULT 절을 사용해요:
INSERT INTO films VALUES
('UA502', 'Bananas', 105, DEFAULT, 'Comedy', '82 minutes');
INSERT INTO films (code, title, did, date_prod, kind)
VALUES ('T_601', 'Yojimbo', 106, DEFAULT, 'Drama');
전적으로 기본값으로 구성된 행을 삽입하려면:
INSERT INTO films DEFAULT VALUES;
다중 행 VALUES 문법으로 여러 행을 삽입하려면:
INSERT INTO films (code, title, did, date_prod, kind) VALUES
('B6717', 'Tampopo', 110, '1985-02-10', 'Comedy'),
('HG120', 'The Dinner Game', 140, DEFAULT, 'Comedy');
이 예제는 films와 같은 열 구성을 가진 tmp_films 테이블에서 films 테이블로 몇몇 행을 삽입해요:
INSERT INTO films SELECT * FROM tmp_films WHERE date_prod < '2004-05-07';
이 예제는 배열 열에 삽입해요:
-- Create an empty 3x3 gameboard for noughts-and-crosses
INSERT INTO tictactoe (game, board[1:3][1:3])
VALUES (1, '{{" "," "," "},{" "," "," "},{" "," "," "}}');
-- The subscripts in the above example aren't really needed
INSERT INTO tictactoe (game, board)
VALUES (2, '{{X," "," "},{" ",O," "},{" ",X," "}}');
DEFAULT 절이 생성한 시퀀스 번호를 반환하면서 distributors 테이블에 단일 행을 삽입하려면:
INSERT INTO distributors (did, dname) VALUES (DEFAULT, 'XYZ Widgets')
RETURNING did;
Acme Corporation 계정을 관리하는 영업사원의 판매 횟수를 증가시키고, 전체 갱신된 행을 현재 시간과 함께 로그 테이블에 기록하려면:
WITH upd AS (
UPDATE employees SET sales_count = sales_count + 1 WHERE id =
(SELECT sales_person FROM accounts WHERE name = 'Acme Corporation')
RETURNING *
)
INSERT INTO employees_log SELECT *, current_timestamp FROM upd;
적절한 대로 새 distributors를 삽입하거나 갱신하려면. did 열에 나타나는 값을 제한하는 고유 인덱스가 정의되어 있다고 가정해요. 원래 삽입을 위해 제안된 값을 참조하는 데 특수 excluded 테이블이 사용된다는 점에 유의하세요:
INSERT INTO distributors (did, dname)
VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc')
ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname;
위와 같이 새 distributors를 삽입하거나 갱신하면서, 갱신된 기존 값에 대한 정보를 새로 삽입된 데이터와 함께 반환하려면. 비충돌 행의 경우 old_did와 old_dname에 대해 반환된 값이 NULL이라는 점에 유의하세요:
INSERT INTO distributors (did, dname)
VALUES (5, 'Gizmo Transglobal'), (6, 'Associated Computing, Inc')
ON CONFLICT (did) DO UPDATE SET dname = EXCLUDED.dname
RETURNING old.did AS old_did, old.dname AS old_dname,
new.did AS new_did, new.dname AS new_dname;
distributor를 삽입하거나, 삽입 행 트리거가 발생한 후 일치하는 제약 열(들)을 가진 기존의 배제된(excluded) 행이 이미 존재할 때 삽입을 위해 제안된 행에 대해 아무것도 하지 않으려면. 이 예제는 did 열에 나타나는 값을 제한하는 고유 인덱스가 정의되어 있다고 가정해요:
INSERT INTO distributors (did, dname) VALUES (7, 'Redline GmbH')
ON CONFLICT (did) DO NOTHING;
적절한 대로 새 distributors를 삽입하거나 갱신하려면. 이 예제는 did 열에 나타나는 값을 제한하는 고유 인덱스가 정의되어 있다고 가정해요. WHERE 절은 실제로 갱신되는 행을 제한하는 데 사용돼요(갱신되지 않는 기존 행도 어쨌든 여전히 잠깁니다):
-- Don't update existing distributors based in a certain ZIP code
INSERT INTO distributors AS d (did, dname) VALUES (8, 'Anvil Distribution')
ON CONFLICT (did) DO UPDATE
SET dname = EXCLUDED.dname || ' (formerly ' || d.dname || ')'
WHERE d.zipcode <> '21201';
-- Name a constraint directly in the statement (uses associated
-- index to arbitrate taking the DO NOTHING action)
INSERT INTO distributors (did, dname) VALUES (9, 'Antwerp Design')
ON CONFLICT ON CONSTRAINT distributors_pkey DO NOTHING;
가능하면 새 distributor를 삽입하고, 그렇지 않으면 DO NOTHING을 하려면. 이 예제는 is_active Boolean 열이 true로 평가되는 행의 부분 집합에 대해 did 열에 나타나는 값을 제한하는 고유 인덱스가 정의되어 있다고 가정해요:
-- This statement could infer a partial unique index on "did"
-- with a predicate of "WHERE is_active", but it could also
-- just use a regular unique constraint on "did"
INSERT INTO distributors (did, dname) VALUES (10, 'Conrad International')
ON CONFLICT (did) WHERE is_active DO NOTHING;
호환성 (Compatibility)
INSERT는 SQL 표준을 따르지만, RETURNING 절은 PostgreSQL 확장이고, INSERT와 함께 WITH를 사용하는 능력과 ON CONFLICT로 대체 동작을 지정하는 능력도 마찬가지예요. 또한 열 이름 목록이 생략되었지만 모든 열이 VALUES 절이나 query에서 채워지지 않는 경우는 표준에서 허용되지 않아요. ON CONFLICT보다 더 SQL 표준을 따르는 명령문을 선호한다면 MERGE를 참고하세요.
SQL 표준은 OVERRIDING SYSTEM VALUE가 항상 생성되는(generated always) identity 열이 존재할 때만 지정될 수 있다고 규정해요. PostgreSQL은 어떤 경우에도 이 절을 허용하고 적용되지 않으면 무시해요.
query 절의 잠재적 제한 사항은 SELECT 아래에 문서화되어 있어요.
더 알아보기 (Learn more)
INSERT, UPDATE, DELETE를 단일 명령문에서 함께 사용하는 방법은 MERGE 문서를, 삽입 권한과 트리거 동작은 CREATE TABLE과 "Data Manipulation" 절을 함께 보면 좋아요.