Sql 无法选择唯一冲突的原因
我有一个表Sql 无法选择唯一冲突的原因,sql,postgresql,plpgsql,Sql,Postgresql,Plpgsql,我有一个表ContentAddressedFiles,其中散列、大小和扩展名的组合是唯一的。我想创建一个存储过程,当调用该存储过程时,它将使用给定的值向表中插入一条新记录。如果这些值的记录已经存在,我只想返回现有的记录。以下是我的方法: CREATE OR REPLACE FUNCTION INIT_CAF( id_in_case_of_new UUID, _hash VARCHAR(255), _size INTEGER, _extension VARCHAR(255), _mimeType
ContentAddressedFiles
,其中散列
、大小
和扩展名
的组合是唯一的。我想创建一个存储过程,当调用该存储过程时,它将使用给定的值向表中插入一条新记录。如果这些值的记录已经存在,我只想返回现有的记录。以下是我的方法:
CREATE OR REPLACE FUNCTION INIT_CAF( id_in_case_of_new UUID, _hash VARCHAR(255), _size INTEGER, _extension VARCHAR(255), _mimeType VARCHAR(255))
RETURNS "ContentAddressedFiles"
AS $$
DECLARE
caf "ContentAddressedFiles"%ROWTYPE;
BEGIN
INSERT INTO "ContentAddressedFiles" (id, hash, size, extension, "mimeType", "createdAt", "updatedAt")
VALUES( id_in_case_of_new, _hash, _size, _extension, _mimeType, NOW(), NOW() ) RETURNING * INTO caf;
RETURN caf;
EXCEPTION WHEN unique_violation THEN
SELECT * FROM "ContentAddressedFiles" INTO caf WHERE "hash" = _hash AND "size" = _size AND "extension" = _extension;
IF NOT FOUND THEN
RAISE EXCEPTION 'This should never happen.';
END IF;
RETURN caf;
END;
$$ LANGUAGE plpgsql;
但是,当我从并发事务调用过程时,我始终会得到异常:
EXCEPTION: This should never happen.
这怎么可能呢?该过程似乎无法
选择之前插入失败的原因(冲突的不是id
,只是
的元组,如何避免问题的答案是注释;我将在这里解释为什么PostgreSQL会以观察到的方式运行
原因是函数中的INSERT
和SELECT
语句可以看到数据库的不同快照(状态),因为事务在默认隔离级别READ COMMITTED
下运行。在该隔离级别下,每条语句都会获得一个新的数据库快照
对观察到的行为的解释必须是,并发事务删除或修改失败的INSERT
和以下SELECT
语句之间的行,以便在运行SELECT
时,导致INSERT
约束冲突的行不再存在
有两种方法可以解决此问题:
- 选择一个更高的隔离级别:然后两个语句将看到数据库的相同快照,阻止插入的行将由
SELECT
找到,即使它同时已被更改。这没有问题,只是意味着整个事务在sna时逻辑上发生普肖特被带走了
- 将这两条语句作为一条带CTE的语句运行,就像注释建议中给出的解决方案一样。然后它们还会看到相同的数据库快照
所以您看到的是提升的exveption和null,而不是预期的行?。请将您的输出添加到问题中是的,确切地说,当我从不同的事务中同时运行该过程时,我会遇到异常。我想他问的是如何发生的,而不是如何简单地完成此任务。我完全同意带有“否”的“马”给出的链接的效率e 10分钟前,但@DeX3逻辑的缺陷是什么?好吧,用,你可以确定。是的,你是对的,这是最有可能的情况(即使比赛条件的窗口很小)。