我想创建一个表,然后以尽可能简洁的方式用一些值初始化它。
但是,每次我的应用启动时都会执行此脚本,因此插入仅应发生在以前尚未添加的项目上。
我不想在“ 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`);
答案 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
上进行测试