데이터 로딩 중 에러 처리하기 (ON CONFLICT)
대량 데이터 로딩의 드라마틱한 현장에 온 걸 환영해! 오늘은 ON CONFLICT 구문으로 데이터 로딩 중에 생기는 에러를 어떻게 똑똑하게 처리할 수 있는지 배워볼 거야. 이건 마치 비행기 오토파일럿 켜는 거랑 비슷해: 뭔가 잘못돼도 당황하지 않고, 사고 없이 대처할 수 있지. 자, PostgreSQL의 꿀팁들 파헤쳐보자!
누구나 예상치 못한 상황은 싫어하지. 특히 데이터가 안 들어갈 때! 대량 데이터 로딩할 때 자주 만나는 문제들이 있어:
- 데이터 중복. 예를 들어, 테이블에
UNIQUE제약조건이 있는데, 데이터 파일에 중복이 많을 때. - 제약조건 충돌. 예를 들어,
NOT NULL제약이 걸린 컬럼에 빈 값을 넣으려고 하면? 결과는? 에러지. PostgreSQL은 이런 상황에서 항상 엄격해. - 중복된 기본 정보. 테이블에 이미 네 CSV 파일이랑 같은 id 가진 데이터가 있을 수도 있어.
이제 ON CONFLICT로 이런 "지뢰"들을 어떻게 피할 수 있는지 알아보자.
ON CONFLICT로 에러 처리하기
ON CONFLICT 구문은 제약조건(UNIQUE나 PRIMARY KEY 등) 충돌이 생겼을 때 어떻게 할지 정할 수 있어. PostgreSQL은 기존 데이터를 업데이트하거나, 충돌된 행을 그냥 무시할 수 있게 해줘.
기본 ON CONFLICT 구문은 이렇게 생겼어:
INSERT INTO table_name (column1, column2, ...)
VALUES (value1, value2, ...)
ON CONFLICT (conflict_target)
DO UPDATE SET column1 = new_value1, column2 = new_value2;
만약 그냥 충돌을 무시하고 싶으면 DO UPDATE 대신 DO NOTHING을 쓰면 돼.
예시: 충돌 시 데이터 업데이트하기
예를 들어, students 테이블이 있다고 해보자:
CREATE TABLE students (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
age INT
);
이제 새로운 데이터를 넣으려고 하는데, 일부는 이미 DB에 있어:
INSERT INTO students (id, name, age)
VALUES
(1, 'Peter', 22), -- 이 학생은 이미 있어
(2, 'Anna', 20), -- 새로운 학생
(3, 'Mal', 25) -- 새로운 학생
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
age = EXCLUDED.age;
이 예시에서, 이미 추가하려는 ID가 있는 학생이 있으면, 그 학생의 데이터가 업데이트돼:
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
age = EXCLUDED.age;
여기서 EXCLUDED라는 마법 같은 단어를 주목해봐. 이건 "넣으려고 했지만 충돌 때문에 제외된 값"을 의미해.
결과:
id = 1인 학생은 이름과 나이가 업데이트돼.id = 2랑id = 3인 학생은 새로 테이블에 추가돼.
예시: 충돌 무시하기
데이터를 업데이트하지 않고, 충돌된 행만 무시하고 싶으면 DO NOTHING을 써:
INSERT INTO students (id, name, age)
VALUES
(1, 'Peter', 22), -- 이 학생은 이미 있어
(2, 'Anna', 20), -- 새로운 학생
(3, 'Mal', 25) -- 새로운 학생
ON CONFLICT (id) DO NOTHING;
이제 충돌된 행은 그냥 안 들어가고, 나머지는 DB에 잘 들어가.
에러 로그 남기기
가끔은 무시하거나 업데이트만으론 부족할 때가 있어. 예를 들어, 나중에 분석하려고 충돌을 기록해야 할 때가 있지. 그럴 땐 에러 로그용 테이블을 따로 만들 수 있어:
CREATE TABLE conflict_log (
conflict_time TIMESTAMP DEFAULT NOW(),
id INT,
name TEXT,
age INT,
conflict_reason TEXT
);
이제 에러 처리와 로그 남기기를 추가해보자:
INSERT INTO students (id, name, age)
VALUES
(1, 'Peter', 22),
(2, 'Anna', 20),
(3, 'Mal', 25)
ON CONFLICT (id) DO UPDATE SET
name = EXCLUDED.name,
age = EXCLUDED.age
RETURNING EXCLUDED.id, EXCLUDED.name, EXCLUDED.age
INTO conflict_log;
이 마지막 예시는 저장 프로시저 안에서만 동작해. 이게 어떻게 돌아가는지는 나중에 PL-SQL 배울 때 자세히 다룰 거야. 살짝 앞서 나간 거지만, 데이터 로딩 충돌을 해결하는 또 다른 방법(문제 있는 행을 전부 로그로 남기기)을 보여주고 싶었어.
이제 충돌 원인을 분석할 수 있어. 이 테크닉은 복잡한 시스템에서 대량 데이터 로딩할 때 "흔적"을 남기는 게 중요할 때 특히 유용해.
실전 예제
지금까지 배운 걸 한 번에 써보는 간단한 미션! 학생 업데이트가 담긴 CSV 파일이 있고, 이걸 테이블에 넣고 싶다고 해보자:
파일 이름은 students_update.csv
| id | name | age |
|---|---|---|
| 1 | Otto | 23 |
| 2 | Anna | 21 |
| 4 | Wally | 30 |
데이터 로딩과 충돌 처리
- 먼저 임시 테이블
tmp_students를 만들어:
CREATE TEMP TABLE tmp_students (
id INTEGER,
name TEXT,
age INTEGER
);
\COPY로 파일에서 데이터 불러오기:
\COPY tmp_students FROM 'students_update.csv' DELIMITER ',' CSV HEADER
- 임시 테이블에서 실제 테이블로
INSERT ON CONFLICT로 데이터 넣기:
INSERT INTO students (id, name, age)
SELECT id, name, age FROM tmp_students
ON CONFLICT (id) DO UPDATE
SET name = EXCLUDED.name,
age = EXCLUDED.age;
이제 모든 데이터(업데이트도 포함해서, id = 1인 행도!)가 잘 들어갔어.
자주 하는 실수와 예방법
고수 개발자도 실수는 해. 하지만 미리 알고 있으면 시간(그리고 멘탈!)을 엄청 아낄 수 있지.
UNIQUE제약조건 충돌.ON CONFLICT에 올바른 필드를 지정했는지 꼭 확인해. 예를 들어,id대신email을 써야 하는데 잘못 지정하면 PostgreSQL이 그냥 "빠이"하고 쿼리 날려버려.EXCLUDED잘못 쓰기. 이 별칭은 현재 쿼리에서 전달된 값에만 쓸 수 있어. 다른 데서 쓰려고 하면 안 돼.- 컬럼 누락.
SET에 적은 컬럼이 테이블에 실제로 있는지 꼭 확인해. 예를 들어,SET non_existing_column = 'value'이런 식이면 에러 나.
ON CONFLICT를 쓰면 PostgreSQL에서 대량 데이터 로딩이 훨씬 유연하고 안전해져. 충돌 때문에 쿼리가 실패하는 걸 막을 수 있을 뿐 아니라, 데이터 처리 방식을 직접 컨트롤할 수 있지. 사용자(그리고 서버!)가 진짜 고마워할 거야.
GO TO FULL VERSION