在plpgsql中生成唯一的随机字符串

3

我正在尝试编写一个函数来创建变长的唯一随机令牌。然而,我被plpgsql语法难住了。我的意图是创建一个函数,该函数:

  • 以表和列作为输入
  • 生成给定长度和一组特定字符的随机字符串
  • 检查字符串是否已经存在于列中
  • 如果是(这种情况应该很少发生),则生成一个新的随机字符串。
  • 否则,返回随机字符串

我的当前尝试看起来像这样:

CREATE FUNCTION random_token(_table TEXT, _column TEXT, _length INTEGER) RETURNS text AS $$
DECLARE
  alphanum CONSTANT text := 'abcdefghijkmnopqrstuvwxyz23456789';
  range_head CONSTANT integer := 25;
  range_tail CONSTANT integer := 33;
  random_string text;
BEGIN
  REPEAT
    SELECT substring(alphanum from trunc(random() * range_head + 1)::integer for 1) ||
      array_to_string(array_agg(substring(alphanum from trunc(random() * range_tail + 1)::integer for 1)), '')
      INTO random_string FROM generate_series(1, _length - 1);
  UNTIL random_string NOT IN FORMAT('SELECT %I FROM %I WHERE %I = random_string;', _column, _table, _column)
  END REPEAT;
  RETURN random_string;
END
$$ LANGUAGE plpgsql;

然而,这并不起作用,并且给我一个不太有帮助的错误信息:
DatabaseError: error 'ERROR: syntax error at or near "REPEAT"
我尝试了许多变化,但由于不知道语法错误在哪里,所以我陷入了困境。您有什么想法来修复这个函数吗?

1
你在手册的哪里找到了repeat - user330315
你知道什么是 UUID 吗?为什么不直接使用它呢?你几乎永远不会生成冲突,它存储在更紧凑的格式中并具有默认运算符。 - Evan Carroll
这个有什么使用场景? - Evan Carroll
2个回答

4

在plpgsql中没有repeat语句。请使用简单的loop

CREATE OR REPLACE FUNCTION random_token(_table TEXT, _column TEXT, _length INTEGER) RETURNS text AS $$
DECLARE
  alphanum CONSTANT text := 'abcdefghijkmnopqrstuvwxyz23456789';
  range_head CONSTANT integer := 25;
  range_tail CONSTANT integer := 33;
  random_string text;
  ct int;
BEGIN
  LOOP
    SELECT substring(alphanum from trunc(random() * range_head + 1)::integer for 1) ||
      array_to_string(array_agg(substring(alphanum from trunc(random() * range_tail + 1)::integer for 1)), '')
      INTO random_string FROM generate_series(1, _length - 1);
    EXECUTE FORMAT('SELECT count(*) FROM %I WHERE %I = %L', _table, _column, random_string) INTO ct;
    EXIT WHEN ct = 0;
  END LOOP;
  RETURN random_string;
END
$$ LANGUAGE plpgsql;

注意,random_string 应该是 format() 的参数。

更新。根据 Abelisto 的准确提示,对于大表格,这应该更快:

DECLARE
  dup boolean;
...
    EXECUTE FORMAT('SELECT EXISTS(SELECT 1 FROM %I WHERE %I = %L)', _table, _column, random_string) INTO dup;
    EXIT WHEN NOT dup;
...

顺便提一下,“select count(*) ...”不是检查表中值是否存在的最佳方法... - Abelisto
非常感谢,我已经花了半天时间尝试让它工作。 - pehrs
1
@klin 对于开发人员来说最简单,但对于服务器来说不是 :o) - Abelisto
@klin 我已经为你的答案点赞了,所以我不能再点赞第二次了 ;o) - Abelisto

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接