MySQL : 테이블에 존재하지 않는 경우 레코드 삽입


384

다음 쿼리를 실행하려고합니다.

INSERT INTO table_listnames (name, address, tele)
VALUES ('Rupert', 'Somewhere', '022')
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name='value'
);

그러나 이것은 오류를 반환합니다. 기본적으로 레코드의 '이름'필드가 이미 다른 레코드에 존재하면 레코드를 삽입하고 싶지 않습니다. 새 이름이 고유한지 확인하는 방법은 무엇입니까?



12
이 또는 속임수에 대한 현재 답변은 모두 고유 색인을 추가 할 수 있다고 가정합니다. 때때로 결정은 전체 테이블에 적용 할 수없는 비즈니스 로직을 기반으로합니다. 예를 들어 열에 특정 값을 가진 여러 행을 허용하지만 열의 다른 값은 한 행에만 표시 될 수 있습니다. 우리는 어떻게 그것을 달성합니까?
Oscar

답변:


502

이 작업을 수행 할 것을 나는 실제로 같이 제안하고 있지 않다 UNIQUEPiskvor에 의해 제안하고 다른 사람이 그것을 할 수있는 훨씬 더 나은 방법으로 인덱스,하지만 당신은 실제로 당신이 시도했던 일을 할 수 있습니다 :

CREATE TABLE `table_listnames` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) NOT NULL,
  `address` varchar(255) NOT NULL,
  `tele` varchar(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;

레코드를 삽입하십시오.

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

동일한 레코드를 다시 삽입하십시오.

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Rupert', 'Somewhere', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 0 rows affected (0.00 sec)
Records: 0  Duplicates: 0  Warnings: 0

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
+----+--------+-----------+------+

다른 레코드를 삽입하십시오.

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'John'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+--------+-----------+------+
| id | name   | address   | tele |
+----+--------+-----------+------+
|  1 | Rupert | Somewhere | 022  |
|  2 | John   | Doe       | 022  |
+----+--------+-----------+------+

등등...


최신 정보:

#1060 - Duplicate column name두 값이 같은 경우 오류 를 방지하려면 내부 SELECT의 열 이름을 지정해야합니다.

INSERT INTO table_listnames (name, address, tele)
SELECT * FROM (SELECT 'Unknown' AS name, 'Unknown' AS address, '022' AS tele) AS tmp
WHERE NOT EXISTS (
    SELECT name FROM table_listnames WHERE name = 'Rupert'
) LIMIT 1;

Query OK, 1 row affected (0.00 sec)
Records: 1  Duplicates: 0  Warnings: 0

SELECT * FROM `table_listnames`;

+----+---------+-----------+------+
| id | name    | address   | tele |
+----+---------+-----------+------+
|  1 | Rupert  | Somewhere | 022  |
|  2 | John    | Doe       | 022  |
|  3 | Unknown | Unknown   | 022  |
+----+---------+-----------+------+

18
도움을 주셔서 감사합니다. 내 실제 문제는 훨씬 복잡하고 열은 고유 할 수 없으며 기본 키에 의존 할 수 없습니다. 그러나 이것이 바로 내가 찾던 것입니다.
Rupert

2
@Piskovar : 동의합니다. @Rupert : name가능한 경우 내부 select 문 ( 이 경우)에 참조 된 열을 색인화해야 합니다. - SELECT 'John', 'Doe', '022' FROM table_listnames대신에 할 수 SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp있지만 table_listnames이미 하나 이상의 행이 포함 된 경우에만 작동합니다 . 나는 속도가 다르다는 것을 의심하므로 걱정하지 않을 것입니다.
Mike

3
@ VijayRamamurthy : Select 문의 결과를 삽입하기 때문에 작동합니다. 쿼리를주의해서 읽으십시오.-> WHERE명령문이 SELECT쿼리에 속합니다 . 선택 쿼리는 단일 데이터 행을 반환하거나 (데이터가 삽입 됨) 데이터가 전혀 없습니다 (아무 것도 삽입되지 않음)
Philipp

13
동일한 값을 다른 필드 (예 : SELECT 'Humbert', 'Humbert', '1'내부 선택)에 두 번 삽입하려는 경우에는 작동하지 않는 것 같습니다 . 나는 수신ERROR 1060 (42S21): Duplicate column name
cburgmer

28
@ cburgmer 같은 문제가 발생했습니다 #1060 - Duplicate column name. 해결책을 찾았습니까? 편집 : 찾았습니다. 각 값 뒤에 AS를 추가하십시오.SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Kai Noack

292

INSERT는 WHERE구문을 허용하지 않습니다 .

수행 할 수있는 작업 : UNIQUE INDEX필드에서 고유해야하는 ( name)을 만든 후 다음 중 하나를 사용하십시오.

  • 정상 INSERT(이름이 이미 존재하는 경우 오류 처리)
  • INSERT IGNORE( 이름이 이미 존재하는 경우 자동으로 실패 하여 오류 대신 경고가 발생 함)
  • INSERT ... ON DUPLICATE KEY UPDATE( UPDATE이름이 이미 존재하면 끝 부분에서 실행됩니다. 문서 참조 )

1
경고 메시지가 필요하면 나중에 할 수 있습니다mysql> show warnings\G
fiorentinoing

2
IME의 경우 경고 메시지가 INSERT IGNORE의 정규식과 일치^Duplicate entry '.+' for key '.+'$
user2426679

1
insert ignore를 사용하면 중복으로 발생하지 않은 다른 오류가 무시됩니까?
gepex

1
@gepex 그렇습니다. INSERT IGNOREIMO 사용에있어 큰 문제입니다 .
shmosel

1
참고 : 고유 키 제약 조건의 열 중 하나가 nullable 인 경우이 답변이 최선의 선택이 아닐 수도 있습니다! 제약 조건은 작동하지만 다른 결과를 기대할 수 있습니다. :( "John Doe", NULL), ( "John Doe", NULL)을 삽입하면 두 필드가 모두 고유 키 제약 조건에 있지만 두 행이 생성됩니다. stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol

57

일한 :

INSERT INTO users (full_name, login, password) 
  SELECT 'Mahbub Tito','tito',SHA1('12345') FROM DUAL
WHERE NOT EXISTS 
  (SELECT login FROM users WHERE login='tito');

이것은 Oracle 데이터베이스에서만 작동합니까? en.wikipedia.org/wiki/DUAL_table 이 질문은 특히 MySQL에 관한 것입니다!
매우 불규칙한

1
Oracle 데이터베이스에서 테스트하지 않습니다. MySQL에서 잘 작동하고 테스트되었습니다.
Mahbub Tito

9
배운 "이중"이후 내가 MySQL의에서 사용할 수 있지만 MariaDB,의 MySQL 오픈 소스 분기
매우 불규칙한

3
@HighlyIrregular의 위키 백과 링크 "Microsoft SQL Server, MySQL, PostgreSQL, SQLite 및 Teradata를 비롯한 여러 다른 데이터베이스를 사용하면 테이블이 필요없는 경우 FROM 절을 완전히 생략 할 수 있습니다. 이렇게하면 더미 테이블이 필요하지 않습니다." FROM DUAL없이 MySql (5.7, 중요하다면)에서 동일한 쿼리가 작동하는 것 같습니다.
stannius

1
MariaDB에서 테스트하고 잘 작동했습니다! 감사합니다 @MahbubTito
Jeff Monteiro

27

MySQL은 매우 귀여운 솔루션을 제공합니다.

REPLACE INTO `table` VALUES (5, 'John', 'Doe', SHA1('password'));

고유 한 기본 키 (여기서는 값 5)를 선언했기 때문에 사용하기 매우 쉽습니다.


이를 방지하기 위해 고유 색인을 작성해야합니다. 자세한 정보는 여기에서 : link
Azmeer

매우 위험한. 자신이하고있는 일을 모른다면 시도하지 마십시오. 실제로 시도하지 마십시오. 다른 답변을 확인하십시오.
Jhourlad Estrella 2016

3
일치하는 행을 삭제 한 다음 다시 삽입합니다. 동일한 동작의 안전한 버전은 ON DUPLICTE KEY UPDATE를 사용하여 행을 삭제하고 다시 삽입하지 않고 행을 업데이트합니다. dev.mysql.com/doc/ refman / 5.7 / en / replace.html
Omn

22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

존재하지 않는 경우 레코드를 삽입하고 기존의 유사한 레코드를 무시하는 mysql 쿼리입니다.

----Untested----

4
그것은 나를 위해 작동하지 않습니다, ININS IGNORE INTO emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
pitu

8
삽입 구문과 업데이트 구문이 혼합 된 것 같습니다. 당신은 의미 INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);합니까?
Hobo

MySQL의 매뉴얼에서 @Hobo dev.mysql.com/doc/refman/5.7/en/insert.html의 INSERT 구문 : 그것은 할 수 있습니다 INSERT [...] IGNORE INTO mytable VALUES ...또는INSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Shirkam

"IGNORE 수정자를 사용하면 INSERT 문을 실행하는 동안 발생하는 오류는 무시됩니다." 그래서 기본적으로 당신은 아무것도 해결하지 못했습니다.
Marcelo Agimóvel

18

다음과 같은 방법으로 쉽게 사용할 수 있습니다.

INSERT INTO ... ON DUPLICATE KEY UPDATE ...

이런 방식으로 새로운 원시를 있으며 중복 된 데이터가있는 경우 특정 열을 대체하십시오 (최상의 열은 타임 스탬프).
예를 들면 다음과 같습니다.

CREATE TABLE IF NOT EXISTS Devices (
  id         INT(6)       NOT NULL AUTO_INCREMENT,
  unique_id  VARCHAR(100) NOT NULL PRIMARY KEY,
  created_at VARCHAR(100) NOT NULL,
  UNIQUE KEY unique_id (unique_id),
  UNIQUE KEY id (id)
)
  CHARACTER SET utf8
  COLLATE utf8_unicode_ci;

INSERT INTO Devices(unique_id, time) 
VALUES('$device_id', '$current_time') 
ON DUPLICATE KEY UPDATE time = '$current_time';

13

실제로 테이블에서 고유 인덱스를 얻을 수 없다면 시도해 볼 수 있습니다 ...

INSERT INTO table_listnames (name, address, tele)
    SELECT 'Rupert', 'Somewhere', '022'
        FROM some_other_table
        WHERE NOT EXISTS (SELECT name
                              FROM table_listnames
                              WHERE name='Rupert')
        LIMIT 1;

12

비슷한 문제를 극복하기 위해 고유 열을 갖도록 테이블을 수정했습니다. 당신의 예를 사용하여, 창조시 나는 다음과 같은 것을 가질 것입니다 :

name VARCHAR(20),
UNIQUE (name)

삽입 할 때 다음 쿼리를 사용하십시오.

INSERT IGNORE INTO train
set table_listnames='Rupert'

이 솔루션을 선호합니다.
Ilyas karim

9

이 쿼리는 잘 작동합니다.

INSERT INTO `user` ( `username` , `password` )
    SELECT * FROM (SELECT 'ersks', md5( 'Nepal' )) AS tmp
    WHERE NOT EXISTS (SELECT `username` FROM `user` WHERE `username` = 'ersks' 
    AND `password` = md5( 'Nepal' )) LIMIT 1

다음 쿼리를 사용하여 테이블을 만들 수 있습니다.

CREATE TABLE IF NOT EXISTS `user` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `username` varchar(30) NOT NULL,
    `password` varchar(32) NOT NULL,
    `status` tinyint(1) DEFAULT '0',
    PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

참고 : 첫 번째 쿼리를 사용하기 전에 두 번째 쿼리를 사용하여 테이블을 만드십시오.


md5 비밀번호 해시 .... yikes!
채드 그랜트

4

Brian Hooper : 당신은 요점을 거의 맞았지만 synatx에 오류가 있습니다. 삽입물이 작동하지 않습니다. 내 데이터베이스에서 테스트했으며 올바른 대답은 다음과 같습니다.

INSERT INTO podatki (datum,ura,meritev1,meritev1_sunki,impulzi1,meritev2,meritev2_sunki,impulzi2)
            SELECT '$datum', '$ura', '$meritve1','$sunki1','$impulzi1','$meritve2','$sunki2','$impulzi2'
                FROM dual
                WHERE NOT EXISTS (SELECT datum,ura
                                      FROM podatki
                                      WHERE datum='$datum' and ura='$ura'

y 테이블의 예를 알려 드리겠습니다. 삽입은 Bian Hooper가 쓴 것과 거의 동일하지만 다른 테이블에서 select FROM DUAL을 ont로 설정했다는 점만 다릅니다. Cind 안부, Ivan


2
"이중"이란 무엇입니까? 내장 된 키워드? 나는 브라이언이 말한 것과의 차이점을 보지 못했습니다 ... 편집 : 추가 조사가 혼란스럽고 모순되는 (?) 결과를 얻었습니다. 하지만 SQL DUAL 테이블 페이지는 MySQL이 DUAL 테이블을 지원하지 않는 것을 말한다는 MySQL의 설명서 는 않는다는 것을 말한다. 내 자신의 테스트 unknown table status: TABLE_TYPE결과 쿼리를 생성했지만 메시지를 제공하기 때문에 메시지 가 표시되지 않기 때문에 그렇지 않습니다 . 아마도 MySQL에는 FROM DUAL절이 필요하지 않기 때문에 ?
not2qubit

4
insert into customer_keyskill(customerID, keySkillID)
select  2,1 from dual
where not exists ( 
    select  customerID  from customer_keyskill 
    where customerID = 2 
    and keySkillID = 1 )

듀얼 Oracle 테이블하지 MySQL은있다
고 불규칙한

3

결과를 업데이트하지 않고 삽입하고 있습니다. 기본 열에서 이름 열을 정의하거나 고유하게 설정할 수 있습니다.


3

문제가 있었고 Mike가 조언 한 방법이 부분적으로 효과가 있었고 Dublicate Column name = '0'이라는 오류가 발생하여 쿼리 구문이 다음과 같이 변경되었습니다.

     $tQ = "INSERT  INTO names (name_id, surname_id, sum, sum2, sum3,sum4,sum5) 
                SELECT '$name', '$surname', '$sum', '$sum2', '$sum3','$sum4','$sum5' 
FROM DUAL
                WHERE NOT EXISTS (
                SELECT sum FROM names WHERE name_id = '$name' 
AND surname_id = '$surname') LIMIT 1;";

열 이름에 문제가있었습니다. sum3은 sum4와 ​​같고 mysql은 중복 열 이름을 던졌고이 구문으로 코드를 작성했으며 완벽하게 작동했습니다.


1
DUAL의 소유자는 SYS입니다 (SYS는 데이터 사전을 소유하므로 DUAL은 데이터 사전의 일부입니다). 그러나 DUAL은 모든 사용자가 액세스 할 수 있습니다. 테이블에는 값이 'X'인 DUMMY라는 단일 VARCHAR2 (1) 열이 있습니다. MySQL을 사용하면 테이블의 데이터가 필요없는 쿼리에서 DUAL을 테이블로 지정할 수 있습니다. DUAL 테이블은 Oracle 및 기타 데이터베이스 설치에서 기본적으로 존재하는 특수한 1 행 1 열 테이블입니다. Oracle에서 테이블에는 값이 'X'인 DUMMY라는 단일 VARCHAR2 (1) 열이 있습니다. SYSDATE 또는 USER와 같은 의사 열을 선택하는 데 사용하기에 적합합니다.
Adnan Haider

3

비슷한 문제가 있었고 기존이 아닌 경우 여러 개를 삽입해야했습니다. 위의 예에서 나는이 조합에 왔습니다 ... 누군가가 필요할 수 있도록 여기에 있습니다.

주의 사항 : MSSQL이 필요로하는 모든 곳에서 이름을 정의해야했습니다 ... MySQL도 *와 함께 작동합니다.

INSERT INTO names (name)
SELECT name
FROM
(
  SELECT name
  FROM
  (
     SELECT 'Test 4' as name
  ) AS tmp_single
  WHERE NOT EXISTS
  (
     SELECT name FROM names WHERE name = 'Test 4'
  )
  UNION ALL
  SELECT name
  FROM
  (
     SELECT 'Test 5' as name
  ) AS tmp_single
  WHERE NOT EXISTS
  (
     SELECT name FROM names WHERE name = 'Test 5'
  )
) tmp_all;

MySQL : CREATE TABLE names( OIDint (11) NOT NULL AUTO_INCREMENT, namevarchar (32) COLLATE utf8_unicode_ci NOT NULL, PRIMARY KEY ( OID), UNIQUE KEY name_UNIQUE(name ) ENGINE = InnoDB AUTO_INCREMENT = 1;

또는

MSSQL : CREATE TABLE [names] ([OID] INT IDENTITY (1, 1) NOT NULL, [name] NVARCHAR (32) NOT NULL, 기본 키 클러스터 됨 ([OID] ASC)); 고유하지 않은 색인 만들기 [Index_Names_Name] ON [이름] ([이름] ASC);


2

이 쿼리는 PHP 코드에서 사용될 수 있습니다.

이 테이블에 ID 열이 있으므로이 ID 열을 제외한 모든 열의 중복을 확인해야합니다.

#need to change values
SET @goodsType = 1, @sybType=5, @deviceId = asdf12345SDFasdf2345;    


INSERT INTO `devices` (`goodsTypeId`, `goodsId`, `deviceId`) #need to change tablename and columnsnames
SELECT * FROM (SELECT @goodsType, @sybType, @deviceId) AS tmp
WHERE NOT EXISTS (
    SELECT 'goodsTypeId' FROM `devices` #need to change tablename and columns names
    WHERE `goodsTypeId` = @goodsType
        AND `goodsId` = @sybType
        AND `deviceId` = @deviceId
) LIMIT 1;

이제 SET문자열에 값이 구성된 행이 존재하지 않는 경우에만 새 항목이 추가됩니다.

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