Mariadb 插入表中尚未包含的值
我想创建一个表,然后以尽可能简洁的方式用一些值初始化它 但是,每次我的应用程序启动时都会执行此脚本,因此插入应该只发生在以前未添加的项目上 我不想在“INSERT IGNORE INTO”中使用IGNORE指令,因为我不想忽略意外错误 出于某种原因,INSERT INTO失败,出现“SQL错误(1136):列计数与第1行的值计数不匹配”,即使下面的选择给出了需要添加的值 下面是失败的代码:Mariadb 插入表中尚未包含的值,mariadb,Mariadb,我想创建一个表,然后以尽可能简洁的方式用一些值初始化它 但是,每次我的应用程序启动时都会执行此脚本,因此插入应该只发生在以前未添加的项目上 我不想在“INSERT IGNORE INTO”中使用IGNORE指令,因为我不想忽略意外错误 出于某种原因,INSERT INTO失败,出现“SQL错误(1136):列计数与第1行的值计数不匹配”,即使下面的选择给出了需要添加的值 下面是失败的代码: START TRANSACTION; CREATE TABLE IF NOT EXISTS `privi
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,添加了缺少的唯一约束
编辑2:感谢LukStorms发现错误与自动增量有关,似乎为自动增量列传递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`);
首先,您的应用程序正在尝试双重插入值。它可能不应该这样做(尽管我可以想到一些有效的用例)。考虑一下,这样它就不会尝试添加以前已经添加的数据。如果无法轻松访问实例间状态,请在启动时从数据库中拉出当前列表,然后再决定插入什么 其次,如果希望标签是唯一的,为什么
标签
字段上没有唯一键?目前,INSERT IGNORE
甚至不起作用,因为模式中没有任何东西可以防止重复的标签值。我会问你自己,为什么你需要一个自动递增的ID
:为什么不把标签设为主键呢
然后,如果仍然需要在SQL层执行此重复省略,则可以使用现有主键:
INSERT INTO `privileges` (`label`)
VALUES
('item1'),
('item2')
)
ON DUPLICATE KEY UPDATE `label` = `label`
这个解决方案很难用自动增量ID键实现,因为您的应用程序可能不知道ID是什么。另一个考虑放弃它的原因。
不幸的是
如果您希望保留ID密钥,并且不希望应用程序在启动时执行读取步骤(可能是出于可伸缩性原因),那么老实说,INSERT IGNORE
是您的最佳选择,尽管您仍然需要在标签上至少有一个唯一的密钥才能使其工作。在MariaDb 10.3+中,使用带有值表达式的CTE可以为其指定列名
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)
但不知何故,当插入到具有自动递增字段的表中时,会出现错误。对我来说好像是个虫子
但是,如果在自动递增字段中插入NULL,则会忽略该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);
dbfiddle测试
不过,使用联合选择也有效
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);
小提琴
也许另一种方法是使用临时表(会话到期时该表将消失)
在dbfiddle上进行测试看起来您正在尝试进行幂等迁移?您想从一个列表(item1 item2等)中插入项目,该列表将随着时间的推移而增长,通过运行此脚本,您希望添加所有缺少的项目?语法错误UNION VALUES
?@DanFarrell该脚本是一个web服务,可对其操作的表进行自初始化。它能够以向后兼容的方式升级旧数据库。大多数表都是通过API调用编辑的,但有些表包含只能“手动”增长的“静态”数据。但这些新项目必须包含在下一次自动初始化中。当使用命令行标志--init db启动服务时,会发生此初始化。在现有数据库上运行此操作时,它应该添加缺少的项,但不能删除任何数据。@使用union值选择可以正常工作(请尝试)。只有插入不起作用。警告其他人:值的使用在MariaDB 10.3中是新的;它在MySQL中还不存在,能够为值分配一个列名是非常好的。不幸的是,上面的查询仍然给我相同的SQL 1136错误。@Fay Urgh,似乎是MariaDb中的一个bug或缺少的功能?看看这把小提琴。使用和不使用该id运行它。如果按值插入,则自动增量可以正常工作,但从“选择值”插入时会出现问题?奇怪的顺便说一句,不确定您是否需要将CREATE放入事务中。我无法理解它,但是当我简单地为id参数传递NULL时,自动增量似乎起作用,即插入特权(id,label)选择NULL,label FROM(..)@Fay Lol。这有点有趣。它看起来很反直觉,我甚至没有想到。@Fay猜想我现在必须更新我的答案:p它也适用于CTE。
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);