UPDATE는 기존 행을 수정한다
INSERT가 새로운 행을 추가하는 명령이라면, UPDATE는 이미 존재하는 행을 고치는 명령이다. SQLite UPDATE 문법은 짧으니 통째로 외워두는 편이 좋다:
UPDATE table_name
SET column = value
WHERE condition;
동작하는 예제:
SET은 무엇을 바꿀지, WHERE는 어떤 행을 바꿀지를 정합니다. 나머지 행은 그대로 둡니다.
실무에서 WHERE 절은 사실상 필수입니다
문법적으로 보면 WHERE는 생략해도 됩니다. 하지만 실무에서 이걸 빼먹는 순간, 주니어 개발자의 오후가 통째로 날아갑니다:
UPDATE users SET status = 'inactive';
-- すべてのユーザーが非アクティブになりました
조건을 빼먹으면 모든 행이 매칭 대상이 됩니다. SQLite는 군말 없이 그대로 실행해 버리죠. 그래서 항상 WHERE를 먼저 쓰고 그다음에 SET을 쓰는 습관을 들이는 게 좋습니다. 이 습관 하나만으로도 사고를 크게 줄일 수 있어요.
WHERE 조건이 제대로 잡혔는지 확신이 없다면, 같은 조건으로 먼저 SELECT를 돌려보세요:
같은 조건으로 두 번 실행하는 셈이죠. SELECT는 일종의 예행연습이라고 보면 됩니다.
sqlite 다중 컬럼 update
여러 컬럼을 한 번에 수정할 때는 하나의 SET 안에 콤마로 구분해 나열하면 됩니다. SET은 하나, 컬럼은 여러 개:
데이터베이스 왕복 한 번, 변경된 행 하나, 업데이트된 컬럼 세 개. UPDATE 문 하나로 끝낼 수 있는 일을 굳이 세 번 나눠 쓰지 마세요.
= 우변에 표현식 사용하기
= 뒤에 오는 값은 꼭 리터럴일 필요가 없습니다. 어떤 표현식이든 올 수 있고, 해당 컬럼의 현재 값을 그대로 참조하는 것도 가능합니다.
price * 1.10은 기존 price 값을 읽어 1.1을 곱한 뒤 그 결과를 다시 저장합니다. SQLite는 우변을 계산할 때 이번 UPDATE 문의 다른 할당이 반영되기 전의 행 값을 사용하므로, 여러 컬럼을 동시에 참조해도 안전합니다:
UPDATE products SET price = price * 1.10, stock = stock + price;
-- ここでの右辺の 'price' は更新後の値ではなく、元の価格です。
UPDATE ... FROM: 다른 테이블의 값 가져와서 업데이트하기
SQLite 3.33부터 UPDATE 문에서 FROM 절을 사용할 수 있게 되면서, 테이블 간 데이터를 업데이트하기가 훨씬 깔끔해졌습니다. 두 테이블의 데이터를 동기화할 때 이만한 방법이 없죠:
서브쿼리에서 고객별 합계를 계산하고, 바깥쪽 UPDATE가 그 결과를 id를 기준으로 customers 테이블과 조인합니다. UPDATE ... FROM이 없었다면 컬럼마다 상관 서브쿼리를 일일이 작성해야 했을 텐데, 훨씬 지저분해졌겠죠.
몇 가지 주의할 점을 정리해 둡니다.
- 대상 테이블은
UPDATE뒤에 와야 하며,FROM목록에 넣으면 안 됩니다. - 조인은
WHERE절에서 처리합니다. 여기서는ON키워드를 쓰지 않습니다. FROM쪽에서 여러 행이 매칭될 가능성이 있다면 결과는 보장되지 않습니다. 조인 키가 대상 행마다 최대 한 건만 매칭되도록 설계하세요.
RETURNING으로 변경된 행 바로 확인하기
SQLite 3.35 이상에서는 UPDATE 문 안에서 수정된 행을 바로 반환받을 수 있습니다. 업데이트 직후의 값이 필요할 때 별도의 SELECT 없이 한 번에 처리할 수 있어 편리합니다.
변경된 행을 새 값과 함께 곧바로 돌려받을 수 있습니다. 왕복 쿼리를 한 번 줄여 주고, 동시성 코드에서 흔히 발생하는 경합 조건도 한 부류 해소해 줍니다. RETURNING에 대해서는 이 장 뒷부분에 따로 한 페이지를 할애했습니다.
UPDATE OR REPLACE: 제약 조건 충돌 다루기
업데이트가 UNIQUE 제약을 위반하면 기본 동작은 에러를 내며 문장을 중단(abort)하는 것입니다. OR 절을 쓰면 이 정책을 다른 방식으로 바꿀 수 있습니다.
옵션은 OR ABORT(기본값), OR REPLACE, OR IGNORE, OR FAIL, OR ROLLBACK 다섯 가지가 있습니다. 이 중 위험한 건 REPLACE인데요. 충돌하는 행을 그냥 삭제해 버리기 때문에 외래 키를 타고 연쇄 삭제가 일어날 수 있습니다. "이 유니크 값을 가진 행이 이미 있다면 그걸 버려도 좋다"는 의도가 정말 분명할 때만 쓰세요.
업서트(upsert) 용도라면 전용 구문인 INSERT ... ON CONFLICT가 훨씬 명확합니다. 이 주제는 별도 문서에서 다룹니다.
위험한 update는 트랜잭션으로 묶기
많은 행을 한꺼번에 수정하거나, 여러 개의 UPDATE 문이 모두 성공해야 하는 상황이라면 트랜잭션으로 감싸는 게 좋습니다. 중간에 문제가 생겨도 이전 상태로 롤백할 수 있으니까요:
두 번째 문장이 실패하면(예: 제약 조건에 걸리면) ROLLBACK이 첫 번째 작업까지 되돌립니다. 트랜잭션 없이 작업했다면 송금이 반쪽짜리로 끝나버리겠죠 — Ada는 25 줄었는데 Boris는 그대로인 상태로요. 트랜잭션은 뒤에서 따로 한 챕터를 할애해 다룰 예정이니, 지금은 "이런 게 있고, 대량 업데이트는 거의 항상 트랜잭션 안에서 실행해야 한다" 정도만 기억해 두세요.
자주 하는 실수
실제로 많이 당하는 함정들을 짧게 정리해 봤습니다.
WHERE빼먹기 — 모든 행이 업데이트됩니다. 실행 전에 쿼리를 한 번 소리 내어 읽어보세요.WHERE에서 잘못된 연산자 사용 —WHERE status = NULL은 아무 것도 매칭하지 않습니다.IS NULL을 써야 해요. 연산자 페이지에서 자세히 다룹니다.- 하나만 나올 거라 기대했는데 여러 행을 반환하는 서브쿼리로 업데이트하기.
LIMIT 1을 붙이거나 서브쿼리를 집계 함수로 감싸지 않으면 에러가 나거나 예상치 못한 결과를 보게 됩니다. - UPDATE OR REPLACE와 UPSERT 헷갈리기.
OR REPLACE는 충돌하는 행을 삭제합니다. 반면INSERT ... ON CONFLICT DO UPDATE는 그 자리에서 값을 수정하죠. 완전히 다른 동작입니다.
다음: DELETE
UPDATE가 행을 수정하는 명령이라면, DELETE는 행을 지우는 명령입니다. WHERE를 신중하게 다뤄야 한다는 원칙은 똑같이 적용되고, "먼저 SELECT로 확인하기" 습관 역시 똑같은 종류의 참사로부터 여러분을 구해줄 겁니다. 다음 페이지에서 이어집니다.
자주 묻는 질문
SQLite UPDATE 문의 기본 문법은 어떻게 되나요?
UPDATE table_name SET column = value WHERE condition; 형태입니다. SET 절에는 바꾸려는 컬럼과 새 값을 적고, WHERE 절에서 어떤 행을 바꿀지 지정합니다. WHERE를 빼먹으면 테이블의 모든 행이 한 번에 수정되니 항상 조심하세요.
한 번에 여러 컬럼을 수정하려면 어떻게 하나요?
SET 절 안에서 콤마로 구분해서 나열하면 됩니다. 예: UPDATE users SET name = 'Ada', email = 'ada@x.com' WHERE id = 1;. 쿼리 한 방으로 여러 컬럼을 동시에 갱신할 수 있어서 굳이 SET을 컬럼마다 따로 쓸 필요가 없습니다.
다른 테이블의 값을 가져와서 UPDATE할 수 있나요?
네, SQLite 3.33부터 지원되는 UPDATE ... FROM 구문을 쓰면 됩니다. UPDATE target SET col = source.col FROM source WHERE target.id = source.id; 처럼 다른 테이블이나 서브쿼리를 조인해서 값을 복사할 수 있어요. 테이블 간 데이터 동기화에 가장 깔끔한 방법입니다.