Answer the question
In order to leave comments, you need to log in
How to make a unique identifier among all database tables in PostgreSQL (globally)?
I have nth number of tables, each of them has an "id" column.
It is necessary that adding a record to any of these tables, the value of the "identifier" field is not repeated among other tables of the database.
I assign this identifier myself from the code, i.e. this is my value obtained according to my rules (which is different from this question).
Of course, I can do it directly from the code, generate an identifier, get all tables, check all records in all (or just try to insert a record, and since the field is unique, the base will report a match, and then delete it), then if the identifier is nothing matched to insert into the desired table. But these are all crutches.
Can I do this with PostgreSQL instead of code?
It's called a global index like here ?
Do I need triggers?
Where to look? What to read? Where in the documentation? Is there a ready solution?
ADDITION: UUID and GUID are not suitable (only numbers are needed in the format, the order should be visible without transformations just by looking at the two identifiers)
ADDITION 2: SEQUENCE is not suitable because it nextval
adds 1, and my identifier is time in nanoseconds (but this that the accuracy can be less or more), it is important that the next value is not just +1, but according to the result of my algorithm. setval
also not suitable because it does not check for uniqueness at the level of all tables, but only at the level of the current one in combination with PRIMARY KEY at the column.
Answer the question
In order to leave comments, you need to log in
Hello. What you are talking about is called a GUID, but there is no unified single algorithm for generating it for everyone. But there are extensions in postgres that can help you and you don't have to generate it at the application level.
This super short article may be useful to you: https://postgrespro.ru/docs/postgrespro/9.5/dataty... and give you an initial incentive and understanding of where to dig further. Good luck)
Update (29.01.20) :
Straight to the point and examples. You can do this, for example, like this:
-- Начнем со схемы данных. На всякий случай проиллюстрирую как может выглядеть идея генерации guid базой, с которой
-- я в самом начале и начал. Но вам, наверное, не будет смысла эту функцию использовать, т.к. сценарий использования
-- у вас другой.
--
-- Обратите также внимание, что я поменял тип на BIGINT. У меня была гипотеза, что вы используете TEXT, т.к., возможно,
-- не знали про BIGINT. Недостаток TEXT в том, что Postgres не построит по нему индекс и выборки по id будут все
-- медленнее и медленнее. Я бы порекомендовал сразу отрефакторить все так, чтобы использовался именно BIGINT.
--
-- Все поля также назвал английскими словами, т.к. это можно считать индустриальным стандартом. NOT NULL добавил
-- по наитию: логика подсказывает, что PRIMARY KEY у каждой таблице обязателен, как и данные. Просто для полноты.
-- Начнем с функции, которая будет генерировать нам ID для примера.
CREATE OR REPLACE FUNCTION guid()
RETURNS BIGINT AS
$BODY$
BEGIN
-- Количество секунд с начала эпохи Линукса и домножаем на какой-то множитель, чтобы увеличить точность
-- и получить 1580307917143.431 вместо 1580307917.143431
RETURN CAST(EXTRACT(EPOCH FROM NOW()) * 1000 AS BIGINT);
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
-- Теперь перейдем к самой схеме данных и создадим ее.
CREATE TABLE users (
id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
pseudonym TEXT NOT NULL
);
CREATE TABLE posts (
id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
content TEXT NOT NULL
);
CREATE TABLE comments (
id BIGINT PRIMARY KEY NOT NULL DEFAULT guid(),
text TEXT NOT NULL
);
-- Для того чтобы повесить CONSTRAINT на id целевых таблиц, понадобится сделать VIEW, котоый будет содержать все
-- id из всех этих таблиц, а также функцию, которую мы сможем использовать для CONSTRAINT. По сути, делаем то же,
-- что делали бы на уровне приложения, но на уровне БД.
CREATE OR REPLACE VIEW all_ids AS
SELECT id FROM users UNION
SELECT id FROM posts UNION
SELECT id FROM comments;
-- Теперь перейдем к функции, которая и будет выполнять всю грязную работу.
CREATE OR REPLACE FUNCTION is_unique_id (BIGINT)
RETURNS BOOLEAN AS 'SELECT CASE WHEN
(SELECT 1
FROM all_ids
WHERE id = $1) > 0
THEN FALSE ELSE TRUE END'
LANGUAGE 'sql' WITH (isstrict);
-- Осталось только повесить CONSTRAINT
ALTER TABLE users ADD CONSTRAINT id CHECK (is_unique_id(id));
ALTER TABLE posts ADD CONSTRAINT id CHECK (is_unique_id(id));
ALTER TABLE comments ADD CONSTRAINT id CHECK (is_unique_id(id));
-- А теперь внимание. Теперь вам придется быть очень внимательным при добавлении таблиц, в рамках которых id должен
-- быть уникален. При добавлении новой таблцы будет необходимо:
-- 1. Пересоздать VIEW, дополнив запрос новыми таблицами.
-- 2. Не забыть повесить аналогичный CONSTRAINT на новую таблицу.
--
-- Также обратите внимание, что при вставке новой записи в любую из таблиц будет проверяться весь созданный VIEW
-- и очень важно чтобы это был не полнотекстовый поиск, а работали индексы, поэтому так важно отрефакторить все в BIGINT.
-- Пришло время тестирования. Вставляем данные.
INSERT INTO users (pseudonym) VALUES ('Первый');
INSERT INTO users (pseudonym) VALUES ('Второй');
INSERT INTO posts (content) VALUES ('О том как надо');
INSERT INTO posts (content) VALUES ('О том как не надо');
INSERT INTO comments (text) VALUES ('Я думаю что решение...');
INSERT INTO comments (text) VALUES ('Хорошо я пропробую сделать...');
-- И глянем что получилось.
SELECT * FROM users;
-- 1580326610797 Первый
-- 1580326611809 Второй
SELECT * FROM posts;
-- 1580326613690 О том как надо
-- 1580326613712 О том как не надо
SELECT * FROM comments;
-- 1580326613779 Я думаю что решение...
-- 1580326613797 Хорошо я пропробую сделать...
-- Время X: тестируем нашу проверку, пытаясь вставить в таблицу users id из таблицы comments:
INSERT INTO users (id, pseudonym) VALUES (1580326613779, 'tiabc');
-- [23514] ERROR: new row for relation "users" violates check constraint "id" Detail: Failing row contains (1580326613779, tiabc)
-- Profit!
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question