Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/sql-server-2008/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Mariadb 插入表中尚未包含的值_Mariadb - Fatal编程技术网

Mariadb 插入表中尚未包含的值

Mariadb 插入表中尚未包含的值,mariadb,Mariadb,我想创建一个表,然后以尽可能简洁的方式用一些值初始化它 但是,每次我的应用程序启动时都会执行此脚本,因此插入应该只发生在以前未添加的项目上 我不想在“INSERT IGNORE INTO”中使用IGNORE指令,因为我不想忽略意外错误 出于某种原因,INSERT INTO失败,出现“SQL错误(1136):列计数与第1行的值计数不匹配”,即使下面的选择给出了需要添加的值 下面是失败的代码: START TRANSACTION; CREATE TABLE IF NOT EXISTS `privi

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

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

我不想在“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,添加了缺少的唯一约束

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