【问题标题】:PostgreSQL Varchar UID to Int UID while preserving uniquenessPostgreSQL Varchar UID 到 Int UID,同时保持唯一性
【发布时间】:2017-12-03 06:07:23
【问题描述】:

假设我有一个独特的列VarChar(32)

例如。 13bfa574e23848b68f1b7b5ff6d794e1.

我想在将列转换为int 时保留它的唯一性。我想我可以将所有字母转换为它们的 ascii 等价物,同时保留数字和字符位置。为此,我将使用翻译功能。

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

我的问题是最初找到 VarChar 列中的所有字母。 我试过了

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

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

感谢任何帮助!

【问题讨论】:

  • 您不能将 VarChar(32) 值映射到 int 以保留唯一性。如果你用 ascii 等效替换所有字母,你将得到至少 32 位数字,而最大整数是 2,147,483,647。
  • Postgres 有特殊的uuid type.
  • 您应该改用 UUID 类型。
  • 我可以使用 bigint 吗?我需要它 int 的原因是因为我正在使用 pgRouting 扩展,并且某些功能需要一个 int 作为输入

标签: sql postgresql postgis


【解决方案1】:

首先,我同意两位评论者的观点,他们说您应该使用 UID 数据类型。

除此之外……

您的 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)
)

然后进行一次性更新,为交叉引用表和实际表的每个 UID 分配一个代理 ID:

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();

【讨论】:

  • 感谢您的帮助。我回去查看了数据的来源,它最初确实是基于 UUID 的。我使用您链接中列出的函数(不是接受的函数)将其转换为整数。效果很好!
【解决方案2】:

创建一个函数,用字符串中不需要的空白替换charter,

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';

【讨论】:

    猜你喜欢
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    • 2018-02-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多