MariaDB / SELECT 결과를 INSERT하는 방법
MariaDB에서 SELECT 결과를 기존 테이블에 넣으려면 INSERT INTO 대상(열 목록) 뒤에 SELECT를 연결합니다. 대상 열과 SELECT 값은 이름이 아니라 순서대로 대응하므로 열 개수와 의미·타입을 맞춰야 합니다. 이 작업은 원본을 조회해 새 행을 삽입하며 원본 행을 삭제하는 이동 작업은 아닙니다.
대상 열과 조회 값의 대응
기본 형태는 INSERT INTO target_table (x,y,z) SELECT a,b,c FROM source_table WHERE 조건입니다. a는 x, b는 y, c는 z에 들어갑니다. SELECT 결과가 여러 행이면 그 행들을 삽입하고, 결과가 0행이면 아무것도 넣지 않습니다. VALUES를 중간에 추가하지 않습니다.
INSERT SELECT 공식 문서에 따라 대상 테이블은 미리 있어야 합니다. 다른 데이터베이스의 테이블은 database_name.table_name으로 지정할 수 있으며 양쪽 권한이 필요합니다. 테이블 구조까지 새로 만들려는 CREATE TABLE ... SELECT와는 목적이 다르고, 조회 결과를 넣는 것만으로 인덱스·제약·트리거를 원본에서 복제하지 않습니다.
실습 테이블과 SELECT 먼저 확인
실습용 learning 데이터베이스에서 CREATE·INSERT·SELECT 권한으로 실행합니다. 아래 이름의 테이블이 있으면 다른 실습 이름을 사용합니다. InnoDB 테이블로 만들어 뒤의 ROLLBACK 확인이 가능하게 합니다. 원본의 S 상태 두 행만 선택하는 예제입니다.
USE learning;
CREATE TABLE select_source_demo (
id INT PRIMARY KEY,
product_name VARCHAR(50) NOT NULL,
stock INT NOT NULL,
status CHAR(1) NOT NULL
) ENGINE=InnoDB;
CREATE TABLE select_target_demo (
id INT AUTO_INCREMENT PRIMARY KEY,
source_id INT NOT NULL UNIQUE,
item_name VARCHAR(50) NOT NULL,
quantity INT NOT NULL,
note VARCHAR(30) NOT NULL DEFAULT 'copied'
) ENGINE=InnoDB;
INSERT INTO select_source_demo VALUES
(1,'Notebook',5,'S'), (2,'Pencil',10,'S'), (3,'Ruler',7,'D');
SELECT id, product_name, stock FROM select_source_demo
WHERE status = 'S' ORDER BY id;
조회 결과는 (1,Notebook,5), (2,Pencil,10)입니다. 이 SELECT에서 대상 행과 값을 먼저 확인합니다. SELECT *로 모든 열을 가져오면 상태 열까지 포함되어 대상 열 개수와 달라질 수 있고, 나중의 스키마 변경으로 대응이 바뀔 수 있으므로 필요한 열을 명시합니다. 문자열 조건 S는 작은따옴표로 작성합니다.
대상의 source_id·item_name·quantity에 원본 id·product_name·stock을 대응시킵니다. 대상 id는 AUTO_INCREMENT로 새로 생성하고 note는 기본값을 사용하므로 삽입 열 목록에서 생략합니다. 기본값이 없고 NULL도 허용하지 않는 필수 열을 빠뜨리면 SQL 모드에 따라 오류 등이 발생할 수 있으니 SHOW CREATE TABLE과 실제 정의를 확인합니다.
트랜잭션에서 삽입 결과를 보고 되돌리기
다음 블록의 START TRANSACTION부터 ROLLBACK까지 같은 접속에서 실행합니다. 중간에 CREATE·ALTER·TRUNCATE 등 암시적으로 커밋하는 DDL을 끼우지 않습니다. ROW_COUNT()는 다른 조회 전에 삽입 직후 확인합니다.
START TRANSACTION;
INSERT INTO select_target_demo (source_id, item_name, quantity)
SELECT id, product_name, stock FROM select_source_demo
WHERE status = 'S';
SELECT ROW_COUNT() AS inserted_rows;
SELECT source_id, item_name, quantity, note
FROM select_target_demo ORDER BY source_id;
ROLLBACK;
SELECT COUNT(*) AS rows_after_rollback FROM select_target_demo;
삽입 행 수는 2, 대상 값은 (1,Notebook,5,copied), (2,Pencil,10,copied)입니다. ROLLBACK 뒤 대상 행 수는 0이 됩니다. 원본 세 행은 그대로입니다. AUTO_INCREMENT에서 예약했던 번호는 ROLLBACK으로 다시 채워진다고 보장하지 않으므로 번호가 건너뛰어도 삽입 실패로만 해석하지 않습니다.
ROLLBACK은 트랜잭션을 지원하는 엔진에서 커밋 전 작업을 되돌리는 용도입니다. 비트랜잭션 테이블이나 외부 부작용까지 같은 방식으로 복구하지는 못합니다. 실제 작업에서는 백업·대상 조건과 스토리지 엔진을 먼저 확인하고, 큰 삽입이 오래 유지하는 잠금·로그·복제 지연도 검토합니다. 단순 사전 SELECT 뒤에 원본이 바뀔 수 있다는 점도 고려해야 합니다.
확인한 조건으로 반영하고 재실행 제어
실습 값을 남기려면 같은 삽입을 새 트랜잭션에서 실행하고 결과 확인 후 COMMIT합니다. 실제 업무에서는 아래를 무조건 일괄 붙여 넣기보다 삽입 결과가 맞는지 확인한 다음 COMMIT 또는 ROLLBACK 중 하나를 선택합니다.
START TRANSACTION;
INSERT INTO select_target_demo (source_id, item_name, quantity)
SELECT id, product_name, stock FROM select_source_demo
WHERE status = 'S';
SELECT ROW_COUNT() AS inserted_rows;
SELECT source_id, item_name, quantity, note
FROM select_target_demo ORDER BY source_id;
COMMIT;
대상 source_id에 UNIQUE 제약이 있으므로 같은 원본을 다시 그대로 넣으면 중복 키 오류가 납니다. 기존 데이터를 덮어쓸 것인지, 건너뛸 것인지, 전체 작업을 중단할 것인지 먼저 정해야 합니다. 아래 예제는 이미 존재하는 source_id를 제외하며 값을 갱신하지 않는 정책입니다.
INSERT INTO select_target_demo (source_id, item_name, quantity)
SELECT s.id, s.product_name, s.stock
FROM select_source_demo AS s
WHERE s.status = 'S'
AND NOT EXISTS (
SELECT 1 FROM select_target_demo AS t WHERE t.source_id = s.id
);
SELECT ROW_COUNT() AS newly_inserted_rows;
앞의 COMMIT 뒤라면 새 삽입 행 수는 0입니다. NOT EXISTS만으로 동시 실행 경쟁까지 해결되는 것은 아닙니다. 이 예제의 UNIQUE 제약이 최종 중복을 막으며 경쟁 시 오류를 어떻게 처리할지도 애플리케이션에서 정해야 합니다. 원본 조회 자체가 같은 키를 여러 번 반환한다면 원본의 중복도 먼저 점검합니다.
중복 오류를 피하려고 INSERT IGNORE를 무조건 붙이지 않습니다. INSERT IGNORE 문서처럼 중복 외의 일부 잘못된 값도 경고로 바뀌거나 보정될 수 있습니다. 경고와 삽입 건수를 검토해야 하며, REPLACE는 기존 행을 삭제한 뒤 넣을 수 있어 같은 동작이 아닙니다. 오류가 났다면 열 개수·타입·문자셋·NOT NULL·기본값·UNIQUE와 외래 키를 확인하고 데이터 의미에 맞게 해결합니다.




