Sisipan Bersyarat MySQL

98

Saya mengalami kesulitan saat membentuk INSERT bersyarat

Saya memiliki x_table dengan kolom (instance, user, item) di mana ID instance unik. Saya ingin menyisipkan baris baru hanya jika pengguna sudah tidak memiliki item tertentu.

Misalnya mencoba memasukkan instance = 919191 pengguna = 123 item = 456

Insert into x_table (instance, user, item) values (919191, 123, 456) 
    ONLY IF there are no rows where user=123 and item=456 

Bantuan atau panduan apa pun ke arah yang benar akan sangat dihargai.

Yang tidak diketahui
sumber

Jawaban:

115

Jika DBMS Anda tidak memberlakukan batasan pada tabel mana yang Anda pilih saat Anda menjalankan penyisipan, coba:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE NOT EXISTS (SELECT * FROM x_table
                             WHERE user = 123 
                               AND item = 456)

Dalam hal ini, dualadalah tabel dengan satu baris saja (awalnya ditemukan di Oracle, sekarang di mysql juga). Logikanya adalah bahwa pernyataan SELECT menghasilkan satu baris data dengan nilai yang diperlukan, tetapi hanya jika nilainya belum ditemukan.

Atau, lihat pernyataan MERGE.

Jonathan Leffler
sumber
1
Apakah dualperlu dibuat sebelumnya dalam kasus ini atau apakah itu semacam tabel "sementara" khusus yang dibuat oleh kueri untuk penggunaan kueri saja?
itsmequinn
8
Jika Anda belum memilikinya dual, Anda perlu membuatnya. CREATE TABLE dual ( dummy CHAR(1) DEFAULT 'x' NOT NULL CHECK (dummy = 'x') PRIMARY KEY ); INSERT INTO dual VALUES('x'); REVOKE ALL ON dual FROM PUBLIC; GRANT SELECT ON dual TO PUBLIC;
Jonathan Leffler
Terima kasih, itu luar biasa untuk diketahui
itsmequinn
11
Perhatikan bahwa, di MySQL, Anda tidak benar-benar perlu memiliki tabel yang disebut 'dual' untuk ada: ini adalah nama tabel khusus yang dapat digunakan untuk memilih apa pun darinya.
Christopher Schultz
3
MySQL "menghormati" konsep "ganda" tetapi sebenarnya tidak ada. Misalnya, jika Anda SELECT COUNT(*) FROM dualselalu mendapatkan 1, dan jika SELECT 'foo' FROM dualAnda selalu mendapatkan foo. Tapi Anda tidak bisa SELECT * FROM dualdan Anda tidak bisa DESCRIBE dualatau semacamnya. Saya belum memeriksanya, tapi menurut saya Anda juga tidak bisa mencabut izin dual. Jadi perlu ditunjukkan bahwa ini berfungsi seperti yang Anda harapkan ... kecuali jika tidak;)
Christopher Schultz
52

Anda juga dapat menggunakan INSERT IGNOREyang secara diam-diam mengabaikan penyisipan alih-alih memperbarui atau menyisipkan baris saat Anda memiliki indeks unik pada (pengguna, item).

Kueri akan terlihat seperti ini:

INSERT IGNORE INTO x_table(instance, user, item) VALUES (919191, 123, 456)

Anda dapat menambahkan indeks unik dengan CREATE UNIQUE INDEX user_item ON x_table (user, item).

MrD
sumber
2
Sesuai dokumentasi MySQL INSERT IGNORE adalah mitra untuk REPLACE ... INSERT IGNORE: pertahankan baris lama. REPLACE: pertahankan baris baru. Keduanya beroperasi dengan kunci unik.
Paul Kenjora
Jawaban terbaik yang pernah ada
jmojico
30

Pernahkah Anda mencoba hal seperti itu?

INSERT INTO x_table
SELECT 919191 as instance, 123 as user, 456 as item
FROM x_table
WHERE (user=123 and item=456)
HAVING COUNT(*) = 0;
Kuil
sumber
Oh ya ! solusi cerdas dan bagus hanya dengan satu pernyataan SELECT
java acm
Bagus. Ini berfungsi untuk postgresql pada percobaan pertama, di mana jawaban yang diterima tampaknya tidak bagi saya.
mightypile
10

Dengan UNIQUE(user, item), lakukan:

Insert into x_table (instance, user, item) values (919191, 123, 456) 
  ON DUPLICATE KEY UPDATE user=123

yang user=123sedikit adalah "no-op" untuk mencocokkan sintaks dari ON DUPLICATEklausa tanpa benar-benar melakukan apa-apa ketika ada duplikat.

Alex Martelli
sumber
1
Saya tidak dapat menyiapkan Unique (pengguna, item) karena mungkin ada beberapa contoh ketika beberapa contoh dengan pengguna dan item yang sama ... hanya saja tidak ketika saya melakukan penyisipan ini.
The Unknown
3

Jika Anda tidak ingin menetapkan batasan unik, ini berfungsi seperti pesona:

INSERT INTO `table` (`column1`, `column2`) SELECT 'value1', 'value2' FROM `table` WHERE `column1` = 'value1' AND `column2` = 'value2' HAVING COUNT(`column1`) = 0

Semoga membantu!

Gew
sumber
Sempurna, terima kasih!
AndyGaskell
2

Apa yang kamu inginkan adalah INSERT INTO table (...) SELECT ... WHERE .... dari manual MySQL 5.6 .

Dalam kasus Anda itu:

INSERT INTO x_table (instance, user, item) SELECT 919191, 123, 456 
WHERE (SELECT COUNT(*) FROM x_table WHERE user=123 AND item=456) = 0

Atau mungkin karena Anda tidak menggunakan logika rumit apa pun untuk menentukan apakah akan menjalankan INSERTatau tidak, Anda bisa menyetel UNIQUEkunci pada kombinasi dua kolom ini dan kemudian menggunakannya INSERT IGNORE.

martin
sumber
Apakah Anda menjalankan jawaban Anda? Saya terus mendapatkan ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'where...(menjalankan 5.6.34)
hlin117
Saya pikir Anda perlu mengatakan from dualsebelumnya where.
hlin117
@ hlin117 Dalam MariaDB FROM DUALadalah default jika tidak ada tabel yang direferensikan ( lihat dokumentasi ini ).
Yeti
1

Jika Anda menambahkan batasan yang (x_table.user, x_table.item) unik, maka memasukkan baris lain dengan pengguna dan item yang sama akan gagal.

misalnya:

mysql> create table x_table ( instance integer primary key auto_increment, user integer, item integer, unique (user, item));
Query OK, 0 rows affected (0.00 sec)

mysql> insert into x_table (user, item) values (1,2),(3,4);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> insert into x_table (user, item) values (1,6);
Query OK, 1 row affected (0.00 sec)

mysql> insert into x_table (user, item) values (1,2);
ERROR 1062 (23000): Duplicate entry '1-2' for key 2
NickZoic
sumber
Itu tidak benar. Mungkin ada beberapa baris dengan pengguna yang sama, tetapi tidak beberapa baris dengan pasangan item-pengguna.
Matthew Flaschen
Ya, ya. Saya salah membacanya. Diedit untuk membuat batasan pada pasangan.
NickZoic
Komentar yang bagus ... saya menjalankan kueri dalam PHP menggunakan mysql_query ("INSERT") dan meninggalkan atau mati Bagian karena itu dosent memperingatkan saya tentang kesalahan sehingga saya tidak perlu melakukan pemeriksaan
Angel.King.47
1

Meskipun ada baiknya untuk memeriksa duplikasi sebelum memasukkan data Anda, saya menyarankan agar Anda meletakkan batasan / indeks unik pada kolom Anda sehingga tidak ada data duplikat yang dapat disisipkan secara tidak sengaja.

Beatles 1692
sumber
1

Anda dapat menggunakan solusi berikut untuk menyelesaikan masalah Anda:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE 123 NOT IN (SELECT user FROM x_table)
Keith Becker
sumber
Meskipun kode ini dapat menjawab pertanyaan, memberikan konteks tambahan tentang bagaimana dan / atau mengapa kode ini memecahkan masalah akan meningkatkan nilai jangka panjang jawaban.
Nic3500
0

Sedikit modifikasi pada respons Alex, Anda juga bisa merujuk nilai kolom yang ada:

Insert into x_table (instance, user, item) values (919191, 123, 456) 
  ON DUPLICATE KEY UPDATE user=user
Danny
sumber
0

Jadi yang ini adalah singkatan dari PostgreSQL

INSERT INTO x_table
SELECT NewRow.*
FROM (SELECT 919191 as instance, 123 as user, 456 as item) AS NewRow
LEFT JOIN x_table
ON x_table.user = NewRow.user AND x_table.item = NewRow.item
WHERE x_table.instance IS NULL
xsubira.dll
sumber
-1
Insert into x_table (instance, user, item) values (919191, 123, 456) 
    where ((select count(*) from x_table where user=123 and item=456) = 0);

Sintaksnya dapat bervariasi tergantung pada DB Anda ...

Rick J.
sumber
2
Untuk apa ini? MySQL?
Umair A.