Как я могу создать уникальную строку для каждой записи в таблице в Postgres?
Скажем, у меня есть стол, подобный сообщениям, который имеет типичные столбцы, такие как id, body, created_at. Я хотел бы создать уникальную строку с созданием каждого сообщения, для использования в чем-то вроде укорачивания URL. Так может быть, 10-значная буквенно-цифровая строка. Он должен быть уникальным в таблице, как первичный ключ.
В идеале для Postgres был бы способ решить обе эти проблемы:
- создать строку
- обеспечить его уникальность
И они должны идти рука об руку, потому что моя цель состоит в том, чтобы не беспокоиться о каком-либо кодексе, обеспечивающем уникальность в моем приложении.
Ответы
Ответ 1
Я не утверждаю, что следующее эффективно, но мы так делали это в прошлом.
CREATE FUNCTION make_uid() RETURNS text AS $$
DECLARE
new_uid text;
done bool;
BEGIN
done := false;
WHILE NOT done LOOP
new_uid := md5(''||now()::text||random()::text);
done := NOT exists(SELECT 1 FROM my_table WHERE uid=new_uid);
END LOOP;
RETURN new_uid;
END;
$$ LANGUAGE PLPGSQL VOLATILE;
make_uid()
может использоваться по умолчанию для столбца в my_table
. Что-то вроде:
ALTER TABLE my_table ADD COLUMN uid text NOT NULL DEFAULT make_uid();
md5(''||now()::text||random()::text)
можно отрегулировать по вкусу. Вы можете рассмотреть encode(...,'base64')
, за исключением того, что некоторые символы, используемые в базе-64, не являются дружественными URL.
Ответ 2
Используйте сеть Feistel. Этот метод эффективно работает для создания уникальных случайных строк в постоянное время без какого-либо столкновения.
Для версии с примерно 2 миллиардами возможных строк (2^31
) из 6 букв см. этот ответ.
Для версии с 63 битами, основанной на bigint
(9223372036854775808
различных возможных значениях), см. этот другой ответ.
Вы можете изменить круглую функцию, как объяснялось в первом ответе, чтобы ввести секретный элемент, чтобы иметь собственную серию строк (не догадаться).
Ответ 3
Посмотрите блог от Брюса. Это дает вам часть пути. Вам нужно будет убедиться, что он еще не существует. Может быть, примирить первичный ключ?
Генерация случайных данных через Sql
"Вам когда-нибудь нужно генерировать случайные данные? Вы можете легко сделать это в клиентских приложениях и на стороне сервера, но в SQL-запросах можно генерировать случайные данные. Следующий запрос генерирует пять строк с строчной буквой длиной в 40 символов строки:"
SELECT
(
SELECT string_agg(x, '')
FROM (
SELECT chr(ascii('a') + floor(random() * 26)::integer)
FROM generate_series(1, 40 + b * 0)
) AS y(x)
)
FROM generate_series(1,5) as a(b);
Ответ 4
Самый простой способ, вероятно, использовать последовательность, чтобы гарантировать уникальность
(поэтому после seq добавьте фиксированное случайное число x цифр):
CREATE SEQUENCE test_seq;
CREATE TABLE test_table (
id bigint NOT NULL DEFAULT (nextval('test_seq')::text || (LPAD(floor(random()*100000000)::text, 8, '0')))::bigint,
txt TEXT
);
insert into test_table (txt) values ('1');
insert into test_table (txt) values ('2');
select id, txt from test_table;
Однако это будет тратить огромное количество записей. (Примечание: max bigInt - 9223372036854775807, если вы используете 8-значное случайное число в конце, вы можете иметь только записи 922337203. Возможно, вам не нужно 8 цифр. Также проверьте максимальное количество для вашей среды программирования!)
В качестве альтернативы вы можете использовать varchar для идентификатора и даже преобразовать вышеуказанный номер с помощью to_hex() или изменить на base36, как показано ниже (но для base36, не пытайтесь разоблачить его для клиента, чтобы избежать появления какой-нибудь забавной строки! ):
PostgreSQL: есть ли функция, которая преобразует base-10 int в строку base-36?
Ответ 5
Используйте первичный ключ в своих данных. Если вам действительно нужна алфавитно-цифровая уникальная строка, вы можете использовать кодировку base-36. В PostgreSQL вы можете использовать эту функцию.
Пример:
select base36_encode(generate_series(1000000000,1000000010));
GJDGXS
GJDGXT
GJDGXU
GJDGXV
GJDGXW
GJDGXX
GJDGXY
GJDGXZ
GJDGY0
GJDGY1
GJDGY2