MySQL: Masukkan catatan jika tidak ada dalam tabel

384

Saya mencoba menjalankan kueri berikut:

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

Tapi ini mengembalikan kesalahan. Pada dasarnya saya tidak ingin memasukkan catatan jika bidang 'nama' catatan sudah ada di catatan lain - bagaimana cara memeriksa apakah nama baru itu unik?

Rupert
sumber
5
kemungkinan duplikat dari Bagaimana 'menyisipkan jika tidak ada' di MySQL?
warren
12
Semua jawaban saat ini untuk ini atau dupes menganggap bahwa Anda dapat menambahkan indeks unik. Terkadang keputusan didasarkan pada logika bisnis yang tidak dapat dipaksakan pada seluruh tabel. Misalnya, Anda mengizinkan beberapa baris dengan nilai tertentu di dalam kolom, tetapi nilai lain di kolom hanya akan diizinkan muncul di satu baris. Bagaimana kita mencapai itu?
Oscar

Jawaban:

502

Saya sebenarnya tidak menyarankan Anda melakukan ini, karena UNIQUEindeks seperti yang disarankan oleh Piskvor dan lainnya adalah cara yang jauh lebih baik untuk melakukannya, tetapi Anda benar-benar dapat melakukan apa yang Anda coba:

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;

Sisipkan catatan:

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  |
+----+--------+-----------+------+

Coba masukkan catatan yang sama lagi:

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  |
+----+--------+-----------+------+

Masukkan catatan yang berbeda:

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  |
+----+--------+-----------+------+

Dan seterusnya...


Memperbarui:

Untuk mencegah #1060 - Duplicate column namekesalahan jika dua nilai mungkin sama, Anda harus memberi nama kolom SELECT bagian dalam:

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  |
+----+---------+-----------+------+
Mike
sumber
18
Terima kasih sudah membantu. Masalah saya yang sebenarnya jauh lebih kompleks dan kolom tidak bisa unik dan saya tidak bisa bergantung pada kunci utama. Tapi ini persis apa yang saya cari.
Rupert
2
@Piskovar: Setuju. @Rupert: Anda harus mengindeks kolom yang dimaksud dalam pernyataan pilih bagian dalam ( name, dalam hal ini), jika memungkinkan. Perhatikan juga yang bisa Anda lakukan SELECT 'John', 'Doe', '022' FROM table_listnames, bukan SELECT * FROM (SELECT 'John', 'Doe', '022') AS tmp- tetapi itu hanya akan berfungsi jika table_listnamessudah mengandung satu atau lebih baris. Saya ragu kecepatannya berbeda, jadi mungkin itu bukan masalah.
Mike
3
@VijayRamamurthy: Ini berfungsi karena Anda Menyisipkan hasil dari pernyataan Select. Baca kueri dengan cermat -> WHEREPernyataan itu milik SELECTkueri. Pilih query baik mengembalikan satu baris data (data dimasukkan) atau tidak ada data sama sekali (tidak ada yang dimasukkan)
Philipp
13
Ini tampaknya tidak berfungsi jika Anda ingin memasukkan nilai yang sama dua kali ke dalam bidang yang berbeda (misalnya SELECT 'Humbert', 'Humbert', '1'dalam pemilihan bagian dalam). Saya menerimaERROR 1060 (42S21): Duplicate column name
cburgmer
28
@cburgmer saya punya masalah yang sama #1060 - Duplicate column name. Sudahkah Anda menemukan solusinya? Sunting: Ditemukan. Tambahkan di belakang setiap nilai sebagai AS:SELECT * FROM (SELECT 'Rupert' as name, 'Rupert' as friend, '022' as number) AS tmp
Kai Noack
292

INSERT tidak mengizinkan WHEREsintaks .

Apa yang dapat Anda lakukan: membuat UNIQUE INDEXbidang yang harus unik ( name), lalu gunakan:

  • normal INSERT(dan menangani kesalahan jika nama sudah ada)
  • INSERT IGNORE(yang akan gagal secara diam-diam menyebabkan peringatan (bukan kesalahan) jika nama sudah ada)
  • INSERT ... ON DUPLICATE KEY UPDATE(yang akan menjalankan UPDATEdi bagian akhir jika nama sudah ada, lihat dokumentasi )
Piskvor meninggalkan gedung
sumber
1
jika Anda perlu mendapatkan pesan peringatan Anda dapat melakukannya kemudianmysql> show warnings\G
fiorentinoing
2
ime, pesan peringatan, dalam kasus INSERT IGNORE, cocok dengan regex dari^Duplicate entry '.+' for key '.+'$
user2426679
1
jika Anda menggunakan sisipkan abaikan, apakah ini akan mengabaikan kesalahan lain yang mungkin terjadi yang bukan karena duplikat?
gepex
1
@ gepex Ya, itu akan. Itu masalah besar dengan menggunakan INSERT IGNOREIMO.
shmosel
1
Catatan: jawaban ini mungkin bukan pilihan terbaik jika salah satu kolom dalam batasan kunci unik dapat dibatalkan! Kendala berfungsi, tetapi Anda mungkin mengharapkan hasil lain :) Memasukkan ("John Doe", NULL), ("John Doe", NULL) menghasilkan dua baris meskipun kedua bidang bersama-sama berada dalam batasan kunci unik. Lihat juga stackoverflow.com/questions/4081783/unique-key-with-nulls
Piemol
57

Bekerja :

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');
Mahbub Tito
sumber
Ini hanya berfungsi untuk database Oracle, bukan? en.wikipedia.org/wiki/DUAL_table Pertanyaan ini khusus tentang MySQL!
Sangat tidak teratur
1
Saya tidak menguji dalam database Oracle. Ini berfungsi dengan baik di MySQL dan diuji.
Mahbub Tito
9
Saya sejak belajar "dual" tersedia di MySQL tetapi tidak di MariaDB, cabang open source dari MySQL
Very Irregular
3
Dari tautan wikipedia @ HighlyIrregular "Beberapa basis data lainnya (termasuk Microsoft SQL Server, MySQL, PostgreSQL, SQLite, dan Teradata) memungkinkan seseorang untuk menghilangkan klausa FROM sepenuhnya jika tidak ada tabel yang diperlukan. Ini menghindari kebutuhan tabel dummy apa pun." Kueri yang sama tampaknya berfungsi di MySql (5.7, jika itu penting) tanpa FROM DUAL.
stannius
1
Diuji dan bekerja dengan baik di MariaDB! Terima kasih @MahbubTito
Jeff Monteiro
27

MySQL memberikan solusi yang sangat lucu:

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

Sangat mudah digunakan karena Anda telah mendeklarasikan kunci primer unik (di sini dengan nilai 5).

sdesvergez
sumber
untuk mencegahnya, Anda perlu membuat indeks unik, info lebih lanjut di sini: tautan
Azmeer
Sangat berbahaya. Jangan berusaha kecuali Anda tahu apa yang Anda lakukan. Sebenarnya, jangan berusaha sama sekali. Periksa jawaban lain.
Jhourlad Estrella
3
Catatan, ini MENGHAPUS setiap baris yang cocok dan kemudian memasukkan kembali, versi yang lebih aman dari perilaku yang sama ini adalah menggunakan ON DUPLICTE KEY UPDATE, yang memperbarui baris-baris perhitungan matematika daripada menghapus dan memasukkan kembali: dev.mysql.com/doc/ refman / 5.7 / id /
replace.html
22
INSERT IGNORE INTO `mytable`
SET `field0` = '2',
`field1` = 12345,
`field2` = 12678;

Di sini permintaan mysql, yang menyisipkan catatan jika tidak ada dan akan mengabaikan catatan serupa yang ada.

----Untested----
Montaser El-sawy
sumber
4
itu tidak bekerja untuk saya, INSERT IGNORE KE emAssignedTagsForEvent SET eventId = '2', defaultTagId = '1';
pitu
8
Sepertinya campuran sisipan dan perbarui sintaksis. Apakah yang Anda maksud INSERT IGNORE INTO `mytable` (`field0`, `field1`, `field2`) values ('2', 12345, 12678);?
Hobo
@Hobo Dari Manual MySQL dev.mysql.com/doc/refman/5.7/en/insert.html INSERT syntax: Dapat INSERT [...] IGNORE INTO mytable VALUES ...AtauINSERT [...] IGNORE INTO mytable SET col_name={expr | DEFAULT},...
Shirkam
"Jika Anda menggunakan pengubah IGNORE, kesalahan yang terjadi saat mengeksekusi pernyataan INSERT diabaikan." Jadi pada dasarnya Anda tidak memecahkan apa pun.
Marcelo Agimóvel
18

Anda dapat dengan mudah menggunakan cara berikut:

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

Dengan cara ini Anda dapat menyisipkan data mentah baru dan jika Anda memiliki data duplikat, ganti kolom tertentu (kolom terbaik adalah cap waktu).
Sebagai contoh :

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';
Arash Hatami
sumber
13

Jika Anda benar-benar tidak bisa mendapatkan indeks unik di atas meja, Anda bisa mencoba ...

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;
Brian Hooper
sumber
12

Untuk mengatasi masalah serupa, saya telah memodifikasi tabel untuk memiliki kolom yang unik. Menggunakan contoh Anda, pada pembuatan saya akan memiliki sesuatu seperti:

name VARCHAR(20),
UNIQUE (name)

dan kemudian gunakan kueri berikut saat memasukkan ke dalamnya:

INSERT IGNORE INTO train
set table_listnames='Rupert'
Obsesif Cookie
sumber
Saya lebih suka solusi ini.
Ilyas karim
9

Kueri ini berfungsi dengan baik:

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

Dan Anda bisa membuat tabel menggunakan kueri berikut:

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 ;

Catatan: Buat tabel menggunakan kueri kedua sebelum mencoba menggunakan kueri pertama.

dakab
sumber
md5 password hash .... ya!
Chad Grant
4

Brian Hooper: Anda hampir mencapai titik tetapi Anda memiliki kesalahan di synatx Anda. Sisipan Anda tidak akan pernah berfungsi. Saya menguji pada database saya dan inilah jawaban yang tepat:

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'

Saya memberikan Anda contoh y tabel. Sisipkan hampir sama seperti yang ditulis Bian Hooper, kecuali bahwa saya meletakkan pilih FROM DUAL dari tabel lain. Salam, Ivan

Ivan Laharnar
sumber
2
Apa itu "ganda"? Kata kunci bawaan? Saya hanya tidak melihat perbedaan dari apa yang dikatakan Brian ... EDIT: Investigasi lebih lanjut mendapat hasil yang beragam dan bertentangan (?). Sementara halaman tabel SQL DUAL mengatakan bahwa MySQL tidak mendukung tabel DUAL, manual MySQL mengatakan itu. Pengujian saya sendiri menunjukkan bahwa tidak, karena ia memberikan unknown table status: TABLE_TYPEpesan, meskipun kueri menghasilkan hasil. Mungkin karena MySQL tidak memerlukan FROM DUALklausa?
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 )
Oms G
sumber
dual adalah tabel Oracle bukan MySQL
Very Irregular
3

Anda memasukkan tidak Memperbarui hasil. Anda dapat menentukan kolom nama di kolom utama atau menetapkannya unik.

Pengguna 1034
sumber
3

Saya mempunyai masalah, dan metode yang disarankan Mike berfungsi sebagian, saya mengalami kesalahan Dublicate Column name = '0', dan mengubah sintaks dari pertanyaan Anda karena ini`

     $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;";

Masalahnya dengan nama kolom. sum3 sama dengan sum4 dan mysql melemparkan nama kolom dublicate, dan saya menulis kode dalam sintaks ini dan bekerja dengan baik,

Hovhannes Babayan
sumber
1
Pemilik DUAL adalah SYS (SYS memiliki kamus data, oleh karena itu DUAL adalah bagian dari kamus data.) Tetapi DUAL dapat diakses oleh pengguna mana pun. Tabel ini memiliki kolom VARCHAR2 (1) tunggal yang disebut DUMMY yang memiliki nilai 'X'. MySQL memungkinkan DUAL untuk ditentukan sebagai tabel dalam kueri yang tidak memerlukan data dari tabel mana pun. Tabel DUAL adalah tabel satu-baris, satu-kolom yang secara default ada di Oracle dan instalasi basis data lainnya. Di Oracle, tabel memiliki satu kolom VARCHAR2 (1) yang disebut DUMMY yang memiliki nilai 'X'. Sangat cocok untuk digunakan dalam memilih kolom semu seperti SYSDATE atau USER.
Adnan haider
3

Saya memiliki masalah yang sama dan saya harus memasukkan beberapa jika tidak ada. Jadi dari contoh di atas saya sampai pada kombinasi ini ... ada di sini kalau-kalau ada orang yang membutuhkannya.

Perhatikan: Saya harus mendefinisikan nama di mana-mana karena diperlukan MSSQL ... MySQL berfungsi dengan * juga.

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) BUKAN NULL AUTO_INCREMENT, namevarchar (32) KUMPULKAN utf8_unicode_ci TIDAK NULL, KUNCI UTAMA ( OID), KUNCI UTAMA ( ), KUNCI UNIK name_UNIQUE( name)) MESIN = InnoDB AUTO_INCREMENT = 1;

atau

MSSQL: CREATE TABLE [names] ([OID] INT IDENTITY (1, 1) BUKAN NULL, [name] NVARCHAR (32) BUKAN NULL, PRIMARY KEY CLUSTERED ([OID] ASC)); CIPTAKAN INDEX NONCLUSTERED UNIK [Index_Names_Name] ON [nama] ([nama] ASC);

Waldemar
sumber
2

Permintaan ini dapat digunakan dalam kode PHP.

Saya memiliki kolom ID di tabel ini, jadi saya perlu memeriksa duplikasi untuk semua kolom kecuali kolom ID ini:

#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;

dan sekarang item baru akan ditambahkan hanya jika tidak ada baris dengan nilai yang dikonfigurasi dalam SETstring

Andrew
sumber