MySQL Conditional Insert


99

ฉันมีช่วงเวลาที่ยากลำบากในการสร้าง INSERT ตามเงื่อนไข

ฉันมี x_table ที่มีคอลัมน์ (เช่นผู้ใช้รายการ) โดยที่ ID อินสแตนซ์ไม่ซ้ำกัน ฉันต้องการแทรกแถวใหม่เฉพาะในกรณีที่ผู้ใช้ไม่มีรายการที่กำหนด

ตัวอย่างเช่นพยายามแทรกอินสแตนซ์ = 919191 user = 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 

ความช่วยเหลือหรือคำแนะนำในทิศทางที่ถูกต้องจะได้รับการชื่นชมมาก

คำตอบ:


118

หาก DBMS ของคุณไม่ได้กำหนดข้อ จำกัด เกี่ยวกับตารางที่คุณเลือกเมื่อคุณดำเนินการแทรกให้ลอง:

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)

ในนี้dualเป็นตารางที่มีแถวเดียวเท่านั้น (พบ แต่เดิมใน Oracle ตอนนี้อยู่ใน mysql ด้วย) ตรรกะคือคำสั่ง SELECT จะสร้างข้อมูลแถวเดียวพร้อมค่าที่ต้องการ แต่เฉพาะเมื่อยังไม่พบค่า

หรือดูที่คำสั่งผสาน


1
ไม่dualจำเป็นที่จะต้องได้รับการสร้างขึ้นก่อนที่มือในกรณีนี้หรือมันเป็นชนิดพิเศษตาราง "ชั่วคราว" บางอย่างที่สร้างขึ้นโดยแบบสอบถามสำหรับการใช้งานของแบบสอบถามเพียงอย่างเดียวหรือไม่
itsmequinn

8
หากคุณยังไม่มีdualคุณต้องสร้างมันขึ้นมา 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

ขอบคุณที่ได้รู้จัก
itsmequinn

11
โปรดทราบว่าใน MySQL คุณไม่จำเป็นต้องมีตารางที่เรียกว่า 'dual' จริงๆ แต่เป็นชื่อตารางพิเศษที่สามารถใช้เพื่อเลือกอะไรก็ได้
Christopher Schultz

3
MySQL "เคารพ" แนวคิดของ "dual" แต่ไม่มีอยู่จริง ตัวอย่างเช่นถ้าคุณSELECT COUNT(*) FROM dualคุณมักจะได้รับ1และถ้าคุณคุณมักจะได้รับSELECT 'foo' FROM dual fooแต่คุณทำไม่ได้SELECT * FROM dualและคุณทำไม่ได้DESCRIBE dualหรืออะไรแบบนั้น ฉันไม่ได้ตรวจสอบ แต่ฉันก็ไม่คิดว่าคุณจะสามารถเพิกถอนสิทธิ์ได้dualเช่นกัน ดังนั้นจึงควรชี้ให้เห็นว่ามันทำงานได้อย่างที่คุณคาดหวัง ... ยกเว้นเมื่อมันไม่เป็นเช่นนั้น)
Christopher Schultz

52

คุณยังสามารถใช้โดยINSERT IGNOREไม่สนใจการแทรกแทนการอัปเดตหรือแทรกแถวเมื่อคุณมีดัชนีเฉพาะบน (ผู้ใช้รายการ)

แบบสอบถามจะมีลักษณะดังนี้:

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

CREATE UNIQUE INDEX user_item ON x_table (user, item)คุณสามารถเพิ่มดัชนีที่ไม่ซ้ำกับ


2
ตามเอกสาร MySQL INSERT IGNORE เป็นคู่ของ REPLACE ... INSERT IGNORE: เก็บแถวเก่า แทนที่: สร้างแถวใหม่ ทั้งสองทำงานบนคีย์ที่ไม่ซ้ำกัน
Paul Kenjora

คำตอบที่ดีที่สุด
jmojico

30

คุณเคยลองอะไรแบบนั้นไหม?

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;

โอ้ใช่ ! โซลูชันที่ชาญฉลาดและดีด้วยคำสั่ง SELECT เพียงคำสั่ง
java acm

ดี. สิ่งนี้ใช้ได้กับ postgresql ในการลองครั้งแรกซึ่งดูเหมือนว่าคำตอบที่ยอมรับนั้นไม่ได้สำหรับฉัน
mightypile

10

ด้วยการUNIQUE(user, item)ทำ:

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

user=123บิต "ไม่-op" เพื่อให้ตรงกับไวยากรณ์ของON DUPLICATEประโยคไม่จริงทำอะไรเมื่อมีรายการที่ซ้ำกัน


1
ฉันไม่สามารถตั้งค่าเฉพาะ (ผู้ใช้รายการ) ได้เนื่องจากอาจมีอินสแตนซ์เมื่อมีหลายอินสแตนซ์ที่มีผู้ใช้และรายการเดียวกัน ... ไม่ใช่ตอนที่ฉันทำส่วนแทรกนี้
The Unknown

3

ในกรณีที่คุณไม่ต้องการกำหนดข้อ จำกัด ที่ไม่ซ้ำกันสิ่งนี้จะใช้ได้ผลเหมือนเสน่ห์:

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

หวังว่าจะช่วยได้!


2

สิ่งที่คุณต้องการคือINSERT INTO table (...) SELECT ... WHERE .... จากคู่มือ MySQL 5.6คู่มือ

ในกรณีของคุณคือ:

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

หรืออาจจะเป็นเพราะคุณไม่ได้ใช้ตรรกะที่ซับซ้อนใด ๆ ที่จะ determiante ว่าจะเรียกใช้INSERTหรือไม่คุณก็สามารถตั้งค่าที่สำคัญในการรวมกันของทั้งสองคอลัมน์แล้วการใช้งานUNIQUEINSERT IGNORE


คุณตอบคำถามของคุณหรือไม่? ฉันได้รับ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...เรื่อย ๆ (วิ่ง 5.6.34)
hlin117

ฉันคิดว่าคุณจำเป็นต้องพูดก่อนfrom dual where
hlin117

@ hlin117 ใน MariaDB FROM DUALเป็นค่าดีฟอลต์หากไม่มีการอ้างอิงตาราง ( ดูเอกสารนี้ )
เยติ

1

หากคุณเพิ่มข้อ จำกัด ที่ (x_table.user, x_table.item) ไม่ซ้ำกันการแทรกแถวอื่นด้วยผู้ใช้เดียวกันและรายการจะล้มเหลว

เช่น:

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

ไม่ถูกต้อง อาจมีหลายแถวที่มีผู้ใช้คนเดียวกัน แต่ไม่ใช่หลายแถวที่มีคู่รายการผู้ใช้
Matthew Flaschen

ใช่ใช่ ฉันอ่านผิด แก้ไขเพื่อสร้างข้อ จำกัด ในคู่
NickZoic

ความคิดเห็นที่ดี ... ฉันเรียกใช้แบบสอบถามใน PHP โดยใช้ mysql_query ("INSERT") และออกจากส่วนหรือตายดังนั้นจึงควรเตือนฉันเกี่ยวกับข้อผิดพลาดดังนั้นฉันจึงไม่ต้องทำการตรวจสอบใด ๆ
Angel.King.47

1

แม้ว่าจะเป็นการดีที่จะตรวจสอบความซ้ำซ้อนก่อนที่จะแทรกข้อมูลของคุณ แต่ฉันขอแนะนำให้คุณใส่ข้อ จำกัด / ดัชนีที่ไม่ซ้ำกันในคอลัมน์ของคุณเพื่อไม่ให้ข้อมูลที่ซ้ำกันถูกแทรกโดยไม่ได้ตั้งใจ


1

คุณสามารถใช้แนวทางต่อไปนี้เพื่อแก้ปัญหาของคุณ:

INSERT INTO x_table(instance, user, item) 
    SELECT 919191, 123, 456
        FROM dual
        WHERE 123 NOT IN (SELECT user FROM x_table)

แม้ว่ารหัสนี้อาจตอบคำถามได้ แต่การให้บริบทเพิ่มเติมเกี่ยวกับวิธีการและ / หรือเหตุผลในการแก้ปัญหาจะช่วยเพิ่มมูลค่าในระยะยาวของคำตอบ
Nic3500

0

การปรับเปลี่ยนการตอบสนองของ Alex เล็กน้อยคุณสามารถอ้างอิงค่าคอลัมน์ที่มีอยู่ได้:

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

0

อันนี้ย่อมาจาก 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

0

วันนี้ฉันพบว่าSELECTคำสั่งสามารถมีWHEREเงื่อนไขได้แม้ว่าจะไม่มีFROMอนุประโยคและไม่อ่านตารางเลยก็ตาม

ทำให้ง่ายต่อการแทรกบางอย่างตามเงื่อนไขโดยใช้โครงสร้างนี้:

SELECT ... INTO @condition;
-- or if you prefer: SET @condition = ...

INSERT INTO myTable (col1, col2) SELECT 'Value 1', 'Value 2' WHERE @condition;

ทดสอบสิ่งนี้บน MySQL 5.7 และ MariaDB 10.3


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

ไวยากรณ์อาจแตกต่างกันไปขึ้นอยู่กับฐานข้อมูลของคุณ ...


2
สิ่งนี้มีไว้เพื่ออะไร? MySQL?
Umair A.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.