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
これに対する現在の回答はすべて、一意のインデックスを追加できると想定しています。時々、決定はテーブル全体に課すことができないビジネスロジックに基づいています。たとえば、列に特定の値を持つ複数の行を許可しますが、列の別の値は1つの行にのみ表示できます。それをどのように達成しますか?
オスカー

次も参照してください:stackoverflow.com/q/1361340/4418
warren

回答:


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 name2つの値が等しい場合のエラーを防ぐには、内部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
助けてくれてありがとう。私の実際の問題ははるかに複雑で、列を一意にすることはできず、主キーに依存することもできません。しかし、これはまさに私が探していたものです。
ルパート

2
@Piskovar:そうですね。@Rupert:可能な場合は、内部のselectステートメント(nameこの場合は)で参照される列にインデックスを付ける必要があります。SELECT 'John', 'Doe', '022' FROM table_listnames代わりにを実行できることにも注意してください。ただし、これは、すでに1つ以上の行が含まれてSELECT * FROM (SELECT 'John', 'Doe', '022') AS tmpいる場合にのみ機能しtable_listnamesます。速度が違うのではないかと思うので、おそらく問題にはなりません。
Mike

3
@VijayRamamurthy:これは、Selectステートメントの結果を挿入するため機能します。クエリを注意深く読んでください-> WHEREステートメントはSELECTクエリに属しています。Selectクエリは、単一のデータ行を返す(データが挿入される)か、まったくデータを返さない(何も挿入されない)
Philipp

13
同じ値を別のフィールドに2回挿入する場合(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
カイノアック

292

INSERTはWHERE構文で許可されていません

何ができるか:UNIQUE INDEX一意である必要があるフィールドで(name)を作成し、次のいずれかを使用します。

  • 通常INSERT(および名前がすでに存在する場合はエラーを処理します)
  • INSERT IGNORE(名前がすでに存在する場合、エラーではなく警告が表示されて失敗します)
  • INSERT ... ON DUPLICATE KEY UPDATEUPDATE名前が既に存在する場合、最後にを実行します。ドキュメントを参照してください

1
警告メッセージを取得する必要がある場合は、後で実行できますmysql> show warnings\G
fiorentinoing

2
imeの場合、警告メッセージはINSERT IGNORE、次の正規表現に一致します^Duplicate entry '.+' for key '.+'$
user2426679

1
挿入無視を使用すると、重複ではない、発生する可能性のある他のエラーが無視されますか?
gepex

1
@gepexはい、そうです。これはINSERT IGNOREIMOの使用に関する大きな問題です。
shmosel

1
注:一意キー制約の列の1つがnull可能である場合、この回答は最善の選択ではない可能性があります。制約は機能しますが、他の結果が期待できます:)挿入( "John Doe"、NULL)、( "John Doe"、NULL)は、両方のフィールドが一意のキー制約内にあるにもかかわらず、2つの行になります。また、stackoverflow.com
questions / 4081783 / unique

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
I「デュアル」を学んだので、MySQLでは利用可能ですが、ないMariaDBはMySQLのオープンソースのブランチで
非常に不規則

3
@HighlyIrregularのウィキペディアリンクから「他のいくつかのデータベース(Microsoft SQL Server、MySQL、PostgreSQL、SQLite、Teradataを含む)を使用すると、テーブルが不要な場合にFROM句を完全に省略できます。これにより、ダミーテーブルが不要になります。」同じクエリは、FROM DUALなしでMySql(重要な場合は5.7)で機能するようです。
stannius

1
MariaDBでテストされ、正常に動作しています!ありがとう@MahbubTito
ジェフモンテイロ

27

MySQLは非常にかわいいソリューションを提供します:

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

一意の主キー(ここでは値5)を宣言しているため、非常に使いやすいです。


これを防ぐには、一意のインデックスを作成する必要があります。詳細については、こちらをご覧ください。リンク
Azmeer、2015年

とても危ない。自分が何をしているかわからない場合は、試みないでください。実際には、絶対に試みないでください。他の回答を確認してください。
Jhourlad Estrella

3
メモは、これは任意の一致する行とその後の再挿入それらを削除し、この同じ現象のより安全なバージョンは行をmathcingなく削除し、それらを再挿入更新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
それは私のために機能しませんでした、INSERT IGNORE INTO emAssignedTagsForEvent SET eventId = '2'、defaultTagId = '1';
pitu 2014年

8
挿入と更新の構文が混在しているように見えます。もしかしてINSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);
Hobo

@Hobo From MySQL Manual 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ステートメントの実行中に発生したエラーは無視されます。」つまり、基本的に何も解決していません。
MarceloAgimóvel17年

18

次の方法で簡単に使用できます。

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

この方法で、新しい rawを挿入し、データが重複している場合は、特定の列を置き換えることができます(最適な列はタイムスタンプです)。
例えば ​​:

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 ;

注:最初のクエリを使用する前に、2番目のクエリを使用してテーブルを作成します。


md5パスワードハッシュ...
チャドグラント

4

ブライアン・フーパー:あなたはほとんどポイントに達しましたが、あなたのシナックスにエラーがあります。挿入は機能しません。私は私のデータベースでテストしました、そしてここに正しい答えがあります:

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テーブルの例を紹介します。InsertはBian Hooperが書いたものとほとんど同じですが、他のテーブルから選択したFROM DUAL ontを配置した点が異なります。敬具、イワン


2
その「デュアル」とは何ですか?組み込みキーワード?ブライアンが言ったこととの違いがわかりません...編集:さらなる調査は複雑で矛盾した(?)結果を得ました。一方でSQL DUAL表のページは、MySQLがDUALテーブルをサポートしていないことを言って、MySQLのマニュアルでは、それがないと言っています。私のテストunknown table status: TABLE_TYPEでは、クエリによって結果が得られたものの、メッセージが表示されるため、表示されません。おそらくMySQLがFROM DUAL節を必要としないためでしょうか?
not2qubit 2013年

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 )

デュアルはMySQLではなくOracleのテーブルです
非常に不規則な

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では、テーブルにはDUMMYと呼ばれる単一のVARCHAR2(1)列があり、値は「X」です。SYSDATEやUSERなどの疑似列を選択する場合に適しています。
アドナンハイダー

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 namesOIDint(11)NOT NULL AUTO_INCREMENT、 namevarchar(32)COLLATE utf8_unicode_ci NOT NULL、PRIMARY KEY(OID)、UNIQUE KEY name_UNIQUEname))ENGINE = InnoDB AUTO_INCREMENT = 1;

または

MSSQL:CREATE TABLE [names]([OID] INT IDENTITY(1、1)NOT NULL、[name] NVARCHAR(32)NOT NULL、PRIMARY KEY CLUSTERED([OID] ASC)); CREATE NOQUE NONCLUSTERED INDEX [Index_Names_Name] ON [names]([name] 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.