“삽입 무시”대“삽입… 중복 키 업데이트시”


833

INSERT많은 행이 있는 문을 실행하는 동안 실패를 유발하는 중복 항목을 건너 뛰고 싶습니다. 몇 가지 연구를 한 결과 내 옵션은 다음 중 하나를 사용하는 것으로 보입니다.

  • ON DUPLICATE KEY UPDATE 어떤 비용으로 불필요한 업데이트를 의미하거나
  • INSERT IGNORE 이는 다른 종류의 실패가 미 고지에 빠지도록 초대하는 것을 의미합니다.

이 가정에서 내가 맞습니까? 중복을 일으킬 수있는 행을 건너 뛰고 다른 행으로 계속 진행하는 가장 좋은 방법은 무엇입니까?

답변:


990

을 사용하는 것이 좋습니다 INSERT...ON DUPLICATE KEY UPDATE.

을 사용 INSERT IGNORE하면 행이 중복 된 키가 있으면 행이 실제로 삽입되지 않습니다. 그러나 문은 오류를 생성하지 않습니다. 대신 경고를 생성합니다. 이러한 경우는 다음과 같습니다.

  • PRIMARY KEY또는 UNIQUE제약 조건이 있는 열에 중복 키 삽입
  • NOT NULL제약 조건이 있는 열에 NULL을 삽입합니다 .
  • 파티션 된 테이블에 행을 삽입하지만 삽입 한 값은 파티션에 맵핑되지 않습니다.

을 사용하는 REPLACE경우 MySQL은 실제로 내부적 으로 DELETE다음을 수행하므로 INSERT예기치 않은 부작용이 있습니다.

  • 새로운 자동 증분 ID가 할당됩니다.
  • 외래 키가있는 종속 행이 삭제되거나 (캐스 케이 딩 외래 키를 사용하는 경우) 그렇지 않을 수 있습니다 REPLACE.
  • DELETE실행되는 트리거 는 불필요하게 실행됩니다.
  • 부작용도 복제본으로 전파됩니다.

수정 : 모두 REPLACEINSERT...ON DUPLICATE KEY UPDATE표준이 아닌, MySQL의 독점 발명의 특정입니다. ANSI SQL 2003은 MERGE동일한 요구를 해결할 수 있는 명령문을 정의 하지만 MySQL은이 MERGE명령문을 지원하지 않습니다 .


사용자가이 게시물을 수정하려고했습니다 (관리자가 편집을 거부했습니다). 수정 사항 INSERT...ON DUPLICATE KEY UPDATE에 새 자동 증분 ID가 할당 되는 소유권 주장을 추가하려고했습니다 . 새 ID가 생성 되는 것은 사실 이지만 변경된 행에는 사용되지 않습니다.

Percona Server 5.5.28로 테스트 한 아래 데모를 참조하십시오. 구성 변수 innodb_autoinc_lock_mode=1(기본값) :

mysql> create table foo (id serial primary key, u int, unique key (u));
mysql> insert into foo (u) values (10);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   10 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1

mysql> insert into foo (u) values (10) on duplicate key update u = 20;
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+

mysql> show create table foo\G
CREATE TABLE `foo` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `u` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `u` (`u`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=latin1

위의 내용은 IODKU 문이 중복을 감지하고 값을 변경하기 위해 업데이트를 호출 함을 보여줍니다 u. (가) 주 AUTO_INCREMENT=3에 id가 생성 된 나타내지 만 행에 사용되지.

반면 REPLACE원래 행을 삭제하고 새 행을 삽입 하여 새 자동 증분 ID를 생성 하고 저장합니다.

mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  1 |   20 |
+----+------+
mysql> replace into foo (u) values (20);
mysql> select * from foo;
+----+------+
| id | u    |
+----+------+
|  3 |   20 |
+----+------+

3
mysql 개발 팀이 ANSI SQL 2003에서 MERGE를 채택하려는 의도가 있는지 궁금합니다.
Lonnie Best

1
@LonnieBest : MERGE 구현을위한 기능 요청은 2005 년에 이루어졌지만 내가 아는 한 진행 또는 계획은 없습니다. bugs.mysql.com/bug.php?id=9018
Bill Karwin

2
오, 잘못된 유형 불일치에 대해 경고 (오류 아님)를 생성하지만 중복 된 복합 기본 키에 대해서는 경고를 생성하지 않는다고 덧붙일 수 있습니다.
Fabrício Matté

11
방금 많은 INSERT ... ON DUPLICATE KEY UPDATE ...진술 로 채워진 테이블을 보았습니다 . 많은 데이터가 복제되어 AI PK의 한 인스턴스가 두 행 사이에서 17,029,941에서 46,271,740으로 증가했습니다. 매번 새로운 AI를 생성하면 범위를 매우 빠르게 채울 수 있으며 정리해야합니다. 이 테이블은 2 주 전입니다!
엔지니어

4
@AntTheKnee, 아, 빅 데이터 시대에 일해야 할 과제.
Bill Karwin

174

이 모든 것이 무엇을 의미하는지 알고 싶다면 여기에 모든 것이 있습니다.

CREATE TABLE `users_partners` (
  `uid` int(11) NOT NULL DEFAULT '0',
  `pid` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`uid`,`pid`),
  KEY `partner_user` (`pid`,`uid`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

기본 키는이 빠른 참조 테이블의 두 열을 기반으로합니다. 기본 키에는 고유 한 값이 필요합니다.

의 시작하자:

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...1 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1);
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1);
...0 row(s) affected

INSERT INTO users_partners (uid,pid) VALUES (1,1) ON DUPLICATE KEY UPDATE uid=uid
...0 row(s) affected

위의 항목은 열을 자체와 동일하게 설정하여 추가 작업을 너무 많이 저장했으며 실제로 업데이트 할 필요는 없습니다.

REPLACE INTO users_partners (uid,pid) VALUES (1,1)
...2 row(s) affected

이제 여러 행 테스트가 있습니다.

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...Error Code : 1062
...Duplicate entry '1-1' for key 'PRIMARY'

INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...3 row(s) affected

콘솔에서 다른 메시지가 생성되지 않았으며 이제 테이블 데이터에 4 개의 값이 있습니다. 나는 같은 경기장에서 테스트 할 수 있도록 (1,1)을 제외한 모든 것을 삭제했습니다.

INSERT INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4) ON DUPLICATE KEY UPDATE uid=uid
...3 row(s) affected

REPLACE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4)
...5 row(s) affected

그래서 당신은 그것을 가지고 있습니다. 데이터가 거의없고 프로덕션이 아닌 새로운 테이블에서이 작업이 모두 수행 되었기 때문에 실행 시간은 미시적이며 관련이 없었습니다. 실제 데이터를 가진 사람은 누구나 기여할 수 있습니다.


중복 키를 모두 실행하고 교체했습니다. 내 테이블의 약 30 %가 중복되는 ~ 120K 행으로 끝났습니다. 중복 키에서 102 초 동안 실행되었고 교체는 105 초 동안 실행되었습니다. 내 경우에는 중복 키를 고수하고 있습니다.
crunkchitis

1
MariaDB 10으로 위의 테스트를 수행하고 실행할 때 경고가 표시되었습니다 INSERT IGNORE INTO users_partners (uid,pid) VALUES (1,1),(1,2),(1,3),(1,4).
Floris

이 모든 것에 어떤 MySQL 버전을 사용 했습니까?
Radu Murzea

41

추가해야 할 중요한 사항 : INSERT IGNORE를 사용할 때 주요 위반 사항이있는 경우 MySQL은 경고를 발생시키지 않습니다!

예를 들어 한 번에 하나의 결함이있는 레코드를 100 개 삽입하려고하면 대화식 모드가됩니다.

Query OK, 99 rows affected (0.04 sec)

Records: 100 Duplicates: 1 Warnings: 0

보다시피 경고 없음! 이 동작은 공식 Mysql Documentation에 잘못 설명되어 있습니다.

스크립트에 정보를 제공해야하는 경우 (키 위반으로 인해) 일부 레코드가 추가되지 않은 경우 mysql_info ()를 호출하여 "중복"값에 대해 구문 분석해야합니다.


6
PHP를 사용 mysqli_affected_rows()하는 경우 INSERT실제로 발생 했는지 확인하는 데 사용해야 합니다 .
Amal Murali

MySQL의 5.5 MariaDB 10 모두 내가 오류가 Cannot add or update a child row: a foreign key constraint fails 더 행 (심지어 유효한 것)이 추가되지 않습니다.
Floris

2
@Floris이 오류는 외래 키 제약 조건 으로 인한 것이지 중복 키로 인한 것이 아닙니다 . MySQL 5.5.28을 사용하고 있습니다. 를 사용할 때 INSERT IGNORE중복 키는 오류나 경고없이 무시됩니다.
toxalot

20

나는 일상적으로를 사용 INSERT IGNORE하며, 당신이 찾고있는 행동의 종류와 똑같이 들립니다. 인덱스 충돌을 일으키는 행이 삽입되지 않고 프로그램을 적절하게 계획하는 한 문제가 발생하지 않습니다.


4
중복 이외의 오류는 무시 할까 걱정됩니다. 이것이 맞습니까? 아니면 INSERT IGNORE는 무시 실패 만 무시합니까? 감사!
토마스 G 헨리

2
오류를 경고로 바꿉니다. 내 대답에서 그러한 사례 목록을 참조하십시오.
Bill Karwin

부끄러운 일입니다. 중복 실패 만 무시하기를 바랍니다.
Lonnie Best

키 위반 은 오류를 유발합니다 ! @Jens '답변에서 내 의견을 참조하십시오.
Floris

1
@Pacerier, 응용 프로그램에서 경고를 확인하는지 여부에 따라 다릅니다. 또는 경고를 확인할 수있는 경우 . 예를 들어, 대부분의 ORM 패키지는 기회를 제공하지 않습니다. 일부 커넥터 (예 : JDBC)도 MySQL API와 분리되어 경고를 확인할 기회가 없습니다.
Bill Karwin

18

나는 이것이 오래되었다는 것을 알고 있지만 INSERT..IGNORE에 대한 정보를 찾으려고 할 때 나와 같은 사람이이 페이지에 도착하는 경우이 메모를 추가합니다.

위에서 언급 한 것처럼 INSERT..IGNORE를 사용하면 INSERT 문을 실행하는 동안 발생하는 오류가 대신 경고로 처리됩니다.

명시 적으로 언급되지 않은 한 가지는 INSERT..IGNORE가 삽입 될 때 유효하지 않은 값이 가장 가까운 값으로 조정된다는 것입니다 (유효하지 않은 값은 IGNORE 키워드를 사용하지 않으면 쿼리가 중단됩니다).


6
나는 당신이 "유효하지 않은 값들"이 무엇을 의미하는지 잘 모르고 무엇으로 수정했는지? 예 또는 추가 설명을 제공 할 수 있습니까?
Marenz

4
"INSERT IGNORE"를 사용할 때 필드에 잘못된 데이터 유형을 삽입하면 필드의 데이터 유형과 일치하도록 데이터가 수정되고 잠재적으로 유효하지 않은 값이 삽입되어 쿼리가 계속 실행됨을 의미합니다. "INSERT"만 사용하면 잘못된 데이터 유형에 대해 오류가 발생하고 쿼리가 중단됩니다. varchar 또는 text 필드에 숫자가 삽입 된 상태에서는 문제가 없지만 숫자 데이터 형식의 필드에 텍스트 문자열을 삽입하면 데이터가 잘못 될 수 있습니다.
codewaggle

2
@Marenz 다른 예 : 테이블에 null이 아닌 열이 있고 "INSERT IGNORE"쿼리에서 해당 열의 값을 지정하지 않으면 엄격한 sql_mode의 활성화 여부에 관계없이 해당 열에 행이 0으로 삽입됩니다. .
Shannon

유효하지 않은 값에 대한 좋은 지적! 이 글은 "INSERT IGNORE"에 대해 배우기에 좋습니다. 5 센트도 남겨 두겠습니다 : medium.com/legacy-systems-diary/… "INSERT IGNORE"를 사용하는 동안 얼마나 조심 해야하는지에 대한 좋은 기사 성명서.
0x49D1

8

중복 키 업데이트시 실제로 는 표준 이 아닙니다 . REPLACE만큼이나 표준입니다. SQL MERGE를 참조하십시오 .

기본적으로 두 명령은 표준 명령의 대체 구문 버전입니다.


1
replace는 삭제 및 삽입을 수행하지만 중복 키 업데이트는 기존 행을 업데이트합니다. 몇 가지 차이점은 다음과 같습니다. 자동 증분 ID, 행 위치, 여러 트리거
ahnbizcad

8

Replace옵션처럼 보입니다. 또는 당신은 확인할 수 있습니다

IF NOT EXISTS(QUERY) Then INSERT

삽입 또는 삭제 후 삽입합니다. IF NOT EXISTS먼저 확인 을하는 경향이 있습니다.


빠른 답변 감사합니다. 나는 모든 곳을 가정하고 있지만 이것이 불필요한 업데이트를 수행한다는 점에서 ON DUPLICATE KEY UPDATE와 유사하다고 가정합니다. 그것은 낭비처럼 보이지만 확실하지 않습니다. 이 중 하나가 작동해야합니다. 누군가가 어느 것이 가장 좋은지 알고 싶습니다.
토마스 G 헨리

6
NTuplip-이 솔루션은 동시 트랜잭션에 의한 인서트의 경쟁 조건에 여전히 열려 있습니다.
Chris KL

REPLACE일치하는 테이블의 모든 행을 삭제 어떤 PRIMARY 또는 UNIQUE, 키 다음 INSERTs . 이것은 IODKU보다 훨씬 더 많은 작업입니다.
Rick James

4

INSERT IGNORE의 잠재적 위험. VARCHAR 값을 더 오래 삽입하려는 경우 열이 다음과 같이 정의되었습니다. 엄격한 모드가 사용 가능한 경우에도 값이 잘리고 삽입됩니다.


3

사용하는 경우 insert ignore가진 SHOW WARNINGS;ID가 중복 된 포함하여 모든 경고와 테이블을 표시합니다 쿼리 세트의 끝에 문을.


SHOW WARNINGS;최신 검색어에만 영향을 미치는 것 같습니다. 하나 이상의 명세서가있는 경우 이전 명세서는 누적되지 않습니다.
Kawu

2

테이블과 기본 키 또는 고유 인덱스의 충돌에 삽입하려면 해당 행을 삽입하는 대신 충돌하는 행을 업데이트합니다.

통사론:

insert into table1 set column1 = a, column2 = b on duplicate update column2 = c;

이제이 insert 문은 앞에서 본 것과 다르게 보일 수 있습니다. 이 insert 문은 a와 b의 값을 가진 table1에 행을 각각 column1과 column2에 삽입하려고합니다.

이 문장을 깊이 이해하자 :

예를 들면 다음과 같습니다. 여기서 column1은 table1의 기본 키로 정의됩니다.

이제 table1에 column1에“a”값을 가진 행이없는 경우. 따라서이 명령문은 table1에 행을 삽입합니다.

이제 table1에 column2에 값 "a"가있는 행이 있습니다. 따라서이 명령문은 행의 column2 값을“c”로 업데이트하며 여기서 column1 값은“a”입니다.

따라서 새 행을 삽입하려면 기본 키 또는 고유 인덱스의 충돌에서 해당 행을 업데이트하십시오.
이 링크에 대해 자세히 알아보십시오


0

INSERT...ON DUPLICATE KEY UPDATE 예기치 않은 예외 관리를 방지하기 위해 선호됩니다.

이 솔루션은 ** 1 개의 고유 제한 조건 ** 만있는 경우에만 작동합니다.

내 경우에는 내가 알고 col1col2고유 복합 인덱스를 확인합니다.

오류를 추적하지만 중복에 대한 예외는 발생하지 않습니다. 성능과 관련하여 MySQL이이를 인식하고 업데이트하지 않는 것과 동일한 값으로 업데이트하는 것이 효율적입니다.

INSERT INTO table
  (col1, col2, col3, col4)
VALUES
  (?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
    col1 = VALUES(col1),
    col2 = VALUES(col2)

이 접근법을 사용하는 아이디어는 phpdelusions.net/pdo 의 의견에서 비롯되었습니다 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.