如果某些值不存在,则MySQL插入多行

时间:2019-07-29 09:55:06

标签: mysql sql

我有以下查询可以正常运行:

INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT 10, 'user_other_unit_moved', now(), 8383
FROM Events
WHERE NOT EXISTS (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
LIMIT 1;

查询的作用是检查“事件”表中是否存在与希望插入的事件类型和单元ID相匹配的行。如果找到现有记录,则不会继续执行INSERT。但是,如果找不到记录,则继续执行INSERT。

这是我的“事件”表的结构:

CREATE TABLE `Events` (
  `event_ID` int(11) NOT NULL,
  `user_ID` int(11) NOT NULL,
  `event_type` varchar(35) NOT NULL,
  `event_creation_datetime` datetime NOT NULL,
  `unit_ID` int(10) UNSIGNED NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

ALTER TABLE `Events`
  ADD PRIMARY KEY (`event_ID`),
  ADD KEY `unit_ID` (`unit_ID`);

ALTER TABLE `Events`
  MODIFY `event_ID` int(11) NOT NULL AUTO_INCREMENT;
COMMIT;

我遇到的问题是在尝试插入多行时试图使上述查询正常工作。我知道如何使用逗号分隔的VALUES插入多行,但是无论我尝试什么,都会遇到语法错误。这是我一直在玩的查询:

INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
VALUES (
(SELECT 10, 'user_other_unit_moved', now(), 8383
FROM Events
WHERE NOT EXISTS (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
 LIMIT 1)),
(SELECT 10, 'user_other_unit_moved', now(), 8380
FROM Events
WHERE NOT EXISTS (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8380)
 LIMIT 1))
);

但是,无论我尝试什么(插入,删除括号等),我都会得到通用的“ SQL语法有错误;”。或“操作数应仅包含1列”。

我还根据其他StackOverflow帖子尝试了这种替代方法:

INSERT IGNORE INTO Events (event_ID, user_ID, event_type, event_creation_datetime, unit_ID)
VALUES
(SELECT (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383), 10, 'user_other_unit_moved', now(), 8383),
(SELECT (SELECT event_ID FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383), 10, 'user_other_unit_moved', now(), 8383);

但是,即使我尝试使用临时表返回结果,也会失败并显示“无法在FROM子句中指定要更新的目标表”。

这只是我的语法错误,还是我试图用查询的方式做一些不可能的事情?如果只是错误,我将如何编写查询以使其按预期工作?请注意,我不想使用多查询-我希望查询作为一个语句来工作。

谢谢, Arj

2 个答案:

答案 0 :(得分:3)

不要使用VALUES,而只能使用INSERT ... SELECT,并且不要使用FROM events
然后UNION ALL

此代码适用于 MySql 5.6

INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT * 
FROM (
  SELECT 10 user_ID, 'user_other_unit_moved' event_type, 
    now() event_creation_datetime, 8383 unit_ID
  UNION ALL
  SELECT 10, 'user_other_unit_moved', now(), 8380
) t
WHERE NOT EXISTS (
  SELECT 1 FROM Events e 
  WHERE e.event_type = t.event_type AND e.unit_ID = t.unit_ID
);

请参见demo

此代码适用于 MySql 5.7 +

INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT * FROM (
  SELECT 10, 'user_other_unit_moved', now(), 8383
  WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
  UNION ALL
  SELECT 10, 'user_other_unit_moved', now(), 8380
  WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8380)
) t 

请参见demo

而对于 MySql 8.0 +

INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
SELECT 10, 'user_other_unit_moved', now(), 8383
WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8383)
UNION ALL
SELECT 10, 'user_other_unit_moved', now(), 8380
WHERE NOT EXISTS (SELECT 1 FROM Events WHERE event_type = 'user_other_unit_moved' AND unit_ID = 8380);

请参见demo

答案 1 :(得分:1)

尽管您可以只用union all来写:

INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
    SELECT x.user_id, x.event_type, now(), x.unit_id
    FROM (SELECT 10 as user_id, 8383 as unit_id, 'user_other_unit_moved' as event_type
         ) x
    WHERE NOT EXISTS (SELECT 1 FROM Events e2 WHERE e2.event_type = x.event_type AND e2.unit_ID = x.unit_ID)
    UNION ALL
    SELECT x.user_id, x.event_type, now(), x.unit_id
    FROM (SELECT 10 as user_id, 8380 as unit_id, 'user_other_unit_moved' as event_type
         ) x
    WHERE NOT EXISTS (SELECT 1 FROM Events e2 WHERE e2.event_type = x.event_type AND e2.unit_ID = x.unit_ID);

我怀疑有更好的方法。如果unit_id的每种事件类型只能有一行,那么您应该使用唯一约束或索引来指定该行:

create unique constraint unq_events_unit_id_event_type on events(unit_id, event_type);

最好让数据库确保完整性。特别是,您的版本受制于竞争条件。并重复插入同一条语句中。

然后,您可以使用on duplicate key来防止重复的行:

INSERT INTO Events (user_ID, event_type, event_creation_datetime, unit_ID)
    VALUES (10, 'user_other_unit_moved', now(), 8383),
           (10, 'user_other_unit_moved', now(), 8380)
    ON DUPLICATE KEY UPDATE unit_ID = VALUES(unit_ID);

该更新实际上不执行任何操作(因为unit_ID已经具有该值)。但这确实防止了错误和重复行的插入。