2017-12-03 167 views
0

说我有一个独特的列VarChar(32)PostgreSQL Varchar UID到Int UID,同时保留唯一性

ex。 13bfa574e23848b68f1b7b5ff6d794e1

我想保留这个唯一性,同时将列转换为int。我想我可以将所有的字母都转换成相应的ascii,同时保留数字和字符位置。为此,我将使用翻译功能。

伪代码:select translate(uid, '[^0-9]', ascii('[^0-9]'))

我的问题是找到所有的字母在VARCHAR列原本。 我试过

select uid, substring(uid from '[^0-9]') from test_table;

但它只返回它遇到的第一个字母。使用上面的例子,我会寻找bfaebfbbffde

任何帮助表示赞赏!

+0

您无法将VarChar(32)值映射到int以保持唯一性。如果您将所有字母替换为ascii等效字符,您将获得至少32位数的数字,而最大整数为2,147,483,647。 – CodeFuller

+1

Postgres有特殊[UUID类型。(https://www.postgresql.org/docs/current/static/datatype-uuid.html) – klin

+1

您应该使用UUID类型,而不是。 –

回答

0

首先,我同意两位评论者说你应该使用UID数据类型。

这一边......

你的UID看起来像一个传统的,因为它不是字母数字,这是十六进制。如果是这样的话,你可以使用这个解决方案的十六进制转换为数值:

PostgreSQL: convert hex string of a very large number to a NUMERIC

通知接受的解决方案(我的,耻辱)是不如其他解决方案上市,作为煤矿将不适用于这个大的十六进制值。

这就是说,这是一个巨大的数字。圣烟。

取决于表中有多少条记录和插入/更新的频率,我会考虑一种截然不同的方法。简而言之,我会创建另一列来存储您的数字ID,其值将由序列确定。

如果你真的想让它刀枪不入,你也可以创建一个交叉引用表来存储将

  1. 重用一个ID,如果它曾经多次(我知道的UID没有关系,但这将涵盖记录被错误删除的情况下,再次出现,并且要保留原来的ID)
  2. 如果UID的重复(比如这是每个UID多个记录的子表),这将覆盖情况以及

如果没有这些应用,你ç应该把它弄歪了一下。

该解决方案将是这个样子:

添加一个ID列,这将是你的数字相当于UID:

alter table test_table 
add column id bigint 

创建一个序列:

CREATE SEQUENCE test_id 

创建交叉参考表(再次,对于虚拟版本不需要):

create table test_id_xref (
    uid varchar(32) not null, 
    id bigint not null, 
    constraint test_id_xref_pk primary key (uid) 
) 

然后做一次更新替代ID分配给每个UID两种交叉引用和实际表:

insert into test_id_xref 
with uids as (
    select distinct uid 
    from test_table 
) 
select uid, nextval ('test_id') 
from uids; 

update test_table tt 
set id = x.id 
from test_id_xref x 
where tt.uid = x.uid; 

最后,未来所有刀片,创建一个触发器来分配下一个值:

CREATE OR REPLACE FUNCTION test_table_insert_trigger() 
    RETURNS trigger AS 
$BODY$ 
BEGIN 
    select t.id 
    from test_id_xref t 
    into NEW.id 
    where t.uid = NEW.uid; 

    if NEW.id is null then 
    NEW.id := nextval('test_id'); 
    insert into test_id_xref values (NEW.uid, NEW.id); 
    end if; 

    return NEW; 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 

CREATE TRIGGER insert_test_table_trigger 
    BEFORE INSERT 
    ON test_table 
    FOR EACH ROW 
    EXECUTE PROCEDURE test_table_insert_trigger(); 
+0

感谢您的帮助。我回过头来看看数据的来源,它的确基于UUID。我使用链接中列出的函数(不是接受的函数)将其转换为整数。很棒! – james

0

创建一个函数替换空白包车,你不是在串需要,

CREATE FUNCTION replace_char(v_string VARCHAR(32) CHARSET utf8) RETURNS VARCHAR(32) 
    DETERMINISTIC 
BEGIN 

    DECLARE v_return_string VARCHAR(32) DEFAULT ''; 
    DECLARE v_remove_char VARCHAR(200) DEFAULT '1,2,3,4,5,6,7,8,9,0'; 
    DECLARE v_length, j INT(3) DEFAULT 0; 

    SET v_length = LENGTH(v_string); 

    WHILE(j < v_length) DO 
    IF (FIND_IN_SET(SUBSTR(v_string, (j+1), 1), v_remove_char) = 0) THEN 
     SET v_return_string = CONCAT(v_return_string, SUBSTR(v_string, (j+1), 1)); 
    END IF; 
    SET j = j+1; 
    END WHILE; 

    RETURN v_return_string; 

END$$ 

DELIMITER ; 

现在你只要东东调用这个函数在查询

select uid, replace_char(uid) from test_table; 

它会给你字符串你需要(bfaebfbbffde

如果你只想int数,即13574238486817567941那么改变变量的值和列数据类型在十进制(50,0)中,十进制可以存储大数,并且有小数点0,所以它将存储int值作为十进制。

v_remove_char = 'a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z';