Postgresql 列默认函数无法创建唯一值

Postgresql 列默认函数无法创建唯一值,postgresql,stored-procedures,insert,unique,default,Postgresql,Stored Procedures,Insert,Unique,Default,我有一张表是这样声明的: CREATE TABLE my_table ( ... guid uuid UNIQUE DEFAULT create_guid('my_table') .... ); 我有一个计算唯一guid的函数 CREATE FUNCTION create_guid(in_table text) RETURNS uuid AS $$ DECLARE v_guid uuid; v_rows int; BEGIN v_guid :

我有一张表是这样声明的:

CREATE TABLE my_table (
    ...
    guid uuid UNIQUE DEFAULT create_guid('my_table')
    ....
);
我有一个计算唯一guid的函数

CREATE FUNCTION create_guid(in_table text) 
RETURNS uuid AS $$
DECLARE 
    v_guid uuid;
    v_rows int;
BEGIN
    v_guid := md5(current_timestamp::text||random()::text);
    EXECUTE 'SELECT 1 FROM ' || quote_ident(in_table) ||'  WHERE guid=' || quote_literal(v_guid);
    GET DIAGNOSTICS v_rows = ROW_COUNT;
    WHILE v_rows > 0 LOOP -- can't use FOUND with EXECUTE
        v_guid := md5(current_timestamp::text||random()::text||v_guid::text);
        EXECUTE 'SELECT 1 FROM ' || quote_ident(in_table) ||'  WHERE guid=' || quote_literal(v_guid);
        GET DIAGNOSTICS v_rows = ROW_COUNT;
    END LOOP;
    RETURN v_guid;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
我从未在我的生产环境中遇到过插入失败,但在我的测试环境中,我可以相当可靠地获得与以下类似的错误:

ERROR: duplicate key value violates unique constraint 'my_table_guid_key'
DETAIL:  Key (guid)=(fed050ad-61c4-d548-3008-2de01301c2fc) already exists.

我意识到当前的时间戳可以是一个相同的值,我想,
random()
也可以,但这不太可能,也不是。即使它们是相同的,while循环不会防止重复吗

当然,我在插入时使用默认值。这怎么会发生?怎么解决呢

我有一个计算唯一guid的函数

这就是你的问题,你已经重新发明了UUID算法生成变体,这是一个有缺陷的变体。相反,只是:

CREATE EXTENSION "uuid-ossp";

SELECT uuid_generate_v4();
。。。产生如下输出:

           uuid_generate_v4           
--------------------------------------
 d3e3a21c-877d-4731-a119-09b25015cb7a
(1 row)
v4 UUID不会发生冲突,即使生日悖论在起作用。包括时间戳实际上增加了冲突的机会,而不是减少冲突

当您使用它时,使用
uuid
数据类型存储uuid,它更快、更紧凑。或者至少将它们作为大端值存储在
bytea
格式中。除了一个填充的、格式化的文本表示之外,任何东西都将是缓慢和浪费的


即使它们是相同的,while循环不会防止重复吗

不,因为它是一种变体,两个并发事务可以各自插入相同的值,但都看不到另一个的值


我对函数进行了清理,使其更具可读性,并且除了非常弱的uuid生成方案之外,没有发现任何明显的逻辑错误,并且它在并发性面前无法工作

CREATE FUNCTION create_guid(in_table text) 
RETURNS uuid AS $$
DECLARE 
    v_guid uuid;
    v_matched boolean = 1;
BEGIN
    WHILE v_matched
    LOOP
      v_guid := md5(current_timestamp::text||random()::text);
      EXECUTE format('SELECT 1 FROM %I  WHERE guid = %L', in_table, v_guid)
      INTO v_matched;
    END LOOP;
    RETURN v_guid;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
它被声明为volatile,它使用两个volatile函数,并执行一个复查循环。如果在目标表上持有独占锁,那么它看起来是正常的。(如果您没有持有独占锁,那么如果存在其他并发插入器,它仍可能失败并出现重复密钥错误)

问题可能出在测试线束或此处可见范围之外的其他部件中

我有一个计算唯一guid的函数

这就是你的问题,你已经重新发明了UUID算法生成变体,这是一个有缺陷的变体。相反,只是:

CREATE EXTENSION "uuid-ossp";

SELECT uuid_generate_v4();
。。。产生如下输出:

           uuid_generate_v4           
--------------------------------------
 d3e3a21c-877d-4731-a119-09b25015cb7a
(1 row)
v4 UUID不会发生冲突,即使生日悖论在起作用。包括时间戳实际上增加了冲突的机会,而不是减少冲突

当您使用它时,使用
uuid
数据类型存储uuid,它更快、更紧凑。或者至少将它们作为大端值存储在
bytea
格式中。除了一个填充的、格式化的文本表示之外,任何东西都将是缓慢和浪费的


即使它们是相同的,while循环不会防止重复吗

不,因为它是一种变体,两个并发事务可以各自插入相同的值,但都看不到另一个的值


我对函数进行了清理,使其更具可读性,并且除了非常弱的uuid生成方案之外,没有发现任何明显的逻辑错误,并且它在并发性面前无法工作

CREATE FUNCTION create_guid(in_table text) 
RETURNS uuid AS $$
DECLARE 
    v_guid uuid;
    v_matched boolean = 1;
BEGIN
    WHILE v_matched
    LOOP
      v_guid := md5(current_timestamp::text||random()::text);
      EXECUTE format('SELECT 1 FROM %I  WHERE guid = %L', in_table, v_guid)
      INTO v_matched;
    END LOOP;
    RETURN v_guid;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
它被声明为volatile,它使用两个volatile函数,并执行一个复查循环。如果在目标表上持有独占锁,那么它看起来是正常的。(如果您没有持有独占锁,那么如果存在其他并发插入器,它仍可能失败并出现重复密钥错误)

问题可能出在测试线束或此处可见范围之外的其他部件中

我有一个计算唯一guid的函数

这就是你的问题,你已经重新发明了UUID算法生成变体,这是一个有缺陷的变体。相反,只是:

CREATE EXTENSION "uuid-ossp";

SELECT uuid_generate_v4();
。。。产生如下输出:

           uuid_generate_v4           
--------------------------------------
 d3e3a21c-877d-4731-a119-09b25015cb7a
(1 row)
v4 UUID不会发生冲突,即使生日悖论在起作用。包括时间戳实际上增加了冲突的机会,而不是减少冲突

当您使用它时,使用
uuid
数据类型存储uuid,它更快、更紧凑。或者至少将它们作为大端值存储在
bytea
格式中。除了一个填充的、格式化的文本表示之外,任何东西都将是缓慢和浪费的


即使它们是相同的,while循环不会防止重复吗

不,因为它是一种变体,两个并发事务可以各自插入相同的值,但都看不到另一个的值


我对函数进行了清理,使其更具可读性,并且除了非常弱的uuid生成方案之外,没有发现任何明显的逻辑错误,并且它在并发性面前无法工作

CREATE FUNCTION create_guid(in_table text) 
RETURNS uuid AS $$
DECLARE 
    v_guid uuid;
    v_matched boolean = 1;
BEGIN
    WHILE v_matched
    LOOP
      v_guid := md5(current_timestamp::text||random()::text);
      EXECUTE format('SELECT 1 FROM %I  WHERE guid = %L', in_table, v_guid)
      INTO v_matched;
    END LOOP;
    RETURN v_guid;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
它被声明为volatile,它使用两个volatile函数,并执行一个复查循环。如果在目标表上持有独占锁,那么它看起来是正常的。(如果您没有持有独占锁,那么如果存在其他并发插入器,它仍可能失败并出现重复密钥错误)

问题可能出在测试线束或此处可见范围之外的其他部件中

我有一个计算唯一guid的函数

这就是你的问题,你已经重新发明了UUID算法生成变体,这是一个有缺陷的变体。相反,只是:

CREATE EXTENSION "uuid-ossp";

SELECT uuid_generate_v4();
。。。产生如下输出:

           uuid_generate_v4           
--------------------------------------
 d3e3a21c-877d-4731-a119-09b25015cb7a
(1 row)
v4 UUID不会发生冲突,即使生日悖论在起作用。包括时间戳实际上增加了冲突的机会,而不是减少冲突

当您使用它时,使用
uuid
数据类型存储uuid,它更快、更紧凑。或者至少将它们作为大端值存储在
bytea
格式中。除了一个填充的、格式化的文本表示之外,任何东西都将是缓慢和浪费的


即使它们是相同的,while循环不会防止重复吗

不,因为它是w的变种