插入表格中尚未存在的值

时间:2018-12-27 23:36:29

标签: mariadb

我想创建一个表,然后以尽可能简洁的方式用一些值初始化它。

但是,每次我的应用启动时都会执行此脚本,因此插入仅应发生在以前尚未添加的项目上。

我不想在“ INSERT IGNORE INTO”中使用IGNORE指令,因为我不想忽略意外错误。

由于某种原因,INSERT INTO失败,并显示“ SQL错误(1136):列数与第1行的值计数不匹配”,即使后面的选择给出了需要添加的值。

这是失败的代码:

START TRANSACTION;

CREATE TABLE IF NOT EXISTS `privileges` (
    `id` TINYINT NOT NULL AUTO_INCREMENT,
    `label` VARCHAR(25) UNIQUE,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `privileges` (`label`)
SELECT `label` FROM (
SELECT NULL AS `label`
UNION VALUES
    ('item1'),
    ('item2')
) X
WHERE `label` IS NOT NULL
AND `label` NOT IN (SELECT `label` FROM `privileges`)

COMMIT;

当前,我要解决此问题,方法是先将值插入临时表中,然后对它进行选择。但是,为什么上述方法不起作用,还有一种更简洁的方法来执行我想做的事情?

我正在使用MariaDB 10.3.9,添加了缺少的UNIQUE约束

编辑2:感谢LukStorms找出错误与AUTO_INCREMENT有关,看来为AUTO_INCREMENT列传递NULL可以解决以下问题:

INSERT INTO `privileges` (id, label)
WITH ITEMS(label) AS (VALUES
    ('users:read'),('users:create'),
    ('clients:read'),('clients:write'),
    ('catalog:read'),('catalog:write'),
    ('cart:read'),('cart:write'),
    ('orders:read'),('orders:write'), ('test1')
) SELECT NULL, label FROM ITEMS i
WHERE label NOT IN (SELECT label FROM `privileges`);

2 个答案:

答案 0 :(得分:1)

首先,您的应用程序尝试将值重复插入。 可能不应该这样做(尽管我可以想到一些有效的用例)。考虑使它不尝试添加以前已经添加的数据。如果您无法轻松访问实例间状态,请在启动时将当前列表从数据库中拉出,然后再确定要插入的内容。

第二,如果希望标签唯一,为什么label字段上没有唯一键?目前,INSERT IGNORE甚至无法工作,因为您的架构中没有任何东西可以防止重复的标签值。我想问问自己为什么您需要自动递增ID:为什么不仅仅拥有label并使其成为主键?

然后,如果您仍然需要在SQL层执行此重复删除操作,则可以use ON DUPLICATE KEY to suck up redundant inserts使用现有的主键:

INSERT INTO `privileges` (`label`)
VALUES
    ('item1'),
    ('item2')
)
ON DUPLICATE KEY UPDATE `label` = `label`

使用您的自动递增ID密钥很难实现此解决方案,因为您的应用程序可能不知道ID是什么。考虑删除它的另一个原因。

很遗憾,there's no ON DUPLICATE KEY IGNORE

如果您想保留ID密钥,并且不希望应用程序在启动时执行读取步骤(也许出于可伸缩性原因),那么INSERT IGNORE是您的最佳选择您仍然需要在label上至少需要一个唯一的密钥才能使它正常工作。

答案 1 :(得分:1)

在MariaDb 10.3+中,将CTE与VALUES表达式配合使用可以让您为其分配列名。

with ITEMS(label) as
(VALUES 
 ('item1')
,('item2'))
select i.label 
from ITEMS i
where not exists (select 1 from privileges p where p.label = i.label)

但是以某种方式插入到具有带有AUTO_INCREMENT字段的表时会给出错误。对我来说似乎是个错误。

但是,当您将NULL插入AUTO_INCREMENT字段时,则NULL将被忽略。但是您自己发现了这种行为。

这可行:

INSERT INTO privileges (id, label)
WITH ITEMS(label) as (
 VALUES ('item1'), ('item2')
)
SELECT null, i.label
FROM ITEMS i
WHERE NOT EXISTS (SELECT 1 FROM privileges p WHERE p.label = i.label);

db <>小提琴here

上进行测试

使用联合选择也可以。

INSERT INTO privileges (label)
SELECT label
FROM (
 SELECT 'item1' as label UNION ALL 
 SELECT 'item2'
) i
WHERE NOT EXISTS (SELECT 1 FROM privileges p WHERE p.label = i.label);

db <>提琴here

也许另一种方法是使用临时表(会话期满后将消失)

CREATE TEMPORARY TABLE tmp_items (label VARCHAR(25) NOT NULL PRIMARY KEY);

INSERT INTO tmp_items (label) VALUES 
 ('item1')
,('item2');

INSERT INTO privileges (label)
SELECT label
FROM tmp_items i
WHERE label NOT IN (SELECT DISTINCT label FROM privileges);

db <>小提琴here

上进行测试