Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/78.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
Sql 无法选择唯一冲突的原因_Sql_Postgresql_Plpgsql - Fatal编程技术网

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逻辑的缺陷是什么?好吧,用,你可以确定。是的,你是对的,这是最有可能的情况(即使比赛条件的窗口很小)。