Компактные или нумеруемые идентификаторы для всех таблиц и reset последовательности до max (id)?
После долгого запуска я получаю все больше и больше дыр в поле id. Идентификатор некоторых таблиц - int32, а последовательность идентификаторов достигает максимального значения. Некоторые из источников Java доступны только для чтения, поэтому я не могу просто изменить тип столбца идентификатора от int32
до long
, что нарушит API.
Я хочу перенумеровать их всех. Это может быть не очень хорошая практика, но хорошее или плохое не касается этого вопроса. Я хочу перенумеровать, особенно те очень длинные идентификаторы, как "61789238", "548273826529524324". Я не знаю, почему они так длинны, но более короткие идентификаторы также легче обрабатывать вручную.
Но это не так просто для компактных идентификаторов вручную из-за ссылок и ограничений.
Поддерживает ли PostgreSQL идентификацию нумерации? Или есть плагин или поддерживающая утилита для этой работы?
Может быть, я могу написать несколько хранимых процедур? Это было бы очень приятно, поэтому я могу запланировать его раз в год.
Ответы
Ответ 1
Предполагая, что ваши идентификаторы генерируются из последовательности bignum
, просто RESTART
последовательность и обновите таблицу с помощью idcolumn = DEFAULT
.
CAVEAT. Если этот столбец id
используется в качестве внешнего ключа другими таблицами, убедитесь, что включен модификатор on update cascade
.
Например:
Создайте таблицу, поместите некоторые данные и удалите среднее значение:
db=# create sequence xseq;
CREATE SEQUENCE
db=# create table foo ( id bigint default nextval('xseq') not null, data text );
CREATE TABLE
db=# insert into foo (data) values ('hello'), ('world'), ('how'), ('are'), ('you');
INSERT 0 5
db=# delete from foo where data = 'how';
DELETE 1
db=# select * from foo;
id | data
----+-------
1 | hello
2 | world
4 | are
5 | you
(4 rows)
Reset ваша последовательность:
db=# ALTER SEQUENCE xseq RESTART;
ALTER SEQUENCE
Обновите свои данные:
db=# update foo set id = DEFAULT;
UPDATE 4
db=# select * from foo;
id | data
----+-------
1 | hello
2 | world
3 | are
4 | you
(4 rows)
Ответ 2
Вопрос старый, но мы получили новый вопрос от отчаянного пользователя на dba.SE, пытаясь применить то, что предлагается здесь. Найти ответ с более подробной информацией и пояснениями там:
принятый в настоящее время ответ не удастся для большинства случаев.
-
Обычно у вас есть ограничение PRIMARY KEY
или UNIQUE
в столбце id
, которое по умолчанию равно NOT DEFERRABLE
. (OP упоминает references and constraints
.) Такие ограничения проверяются после каждой строки, поэтому вы, скорее всего, получаете ошибки уникальное нарушение. Подробности:
-
Как правило, требуется сохранить исходный порядок строк при закрытии пробелов. Но порядок обновления строк - произвольный, что приводит к произвольным номерам. Показанный пример, похоже, сохраняет исходную последовательность, поскольку физическое хранилище все еще совпадает с желаемым порядком (вставленные строки в желаемом порядке только на мгновение раньше), что почти никогда не имеет место в реальных приложениях и полностью ненадежно.
Вопрос сложнее, чем может показаться на первый взгляд. Один (в том числе), если вы можете временно ограничить ограничение PK/UNIQUE (и связанные с ним ограничения FK):
BEGIN;
LOCK tbl;
-- remove all FK constraints to the column
ALTER TABLE tbl DROP CONSTRAINT tbl_pkey; -- remove PK
-- for the simple case without FK references - or see below:
UPDATE tbl t -- intermediate unique violations are ignored now
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id;
-- Update referencing value in FK columns at the same time (if any)
SELECT setval('tbl_id_seq', max(id)) FROM tbl; -- reset sequence
ALTER TABLE tbl ADD CONSTRAINT tbl_pkey PRIMARY KEY(id); -- add PK back
-- add all FK constraints to the column back
COMMIT;
Это также намного быстрее для больших таблиц, потому что проверка ограничений PK (и FK) для каждой строки стоит намного больше, чем удаление ограничений (ограничений) и добавление их (их) назад.
Если в других таблицах имеются ссылки FK, ссылающиеся на tbl.id
, используйте изменяющие данные CTE, чтобы обновить их все.
Пример таблицы fk_tbl
и столбца FK fk_id
:
WITH u1 AS (
UPDATE tbl t
SET id = t1.new_id
FROM (SELECT id, row_number() OVER (ORDER BY id) AS new_id FROM tbl) t1
WHERE t.id = t1.id
RETURNING t.id, t1.new_id -- return old and new ID
)
UPDATE fk_tbl f
SET fk_id = u1.new_id -- set to new ID
FROM u1
WHERE f.fk_id = u1.id; -- match on old ID
Подробнее в ответе на dba.SE.
Ответ 3
новый столбец идентификатора и внешний ключ (ключи), а старые - все еще используются. При некотором (быстром) переименовании приложениям не нужно знать. (Но приложения должны быть неактивными во время последнего этапа переименования)
\i tmp.sql
-- the test tables
CREATE TABLE one (
id serial NOT NULL PRIMARY KEY
, payload text
);
CREATE TABLE two (
id serial NOT NULL PRIMARY KEY
, the_fk INTEGER REFERENCES one(id)
ON UPDATE CASCADE ON DELETE CASCADE
);
-- And the supporting index for the FK ...
CREATE INDEX ON two(the_fk);
-- populate
INSERT INTO one(payload)
SELECT x::text FROM generate_series(1,1000) x;
INSERT INTO two(the_fk)
SELECT id FROM one WHERE random() < 0.3;
-- make some gaps
DELETE FROM one WHERE id % 13 > 0;
-- SELECT * FROM two;
-- Add new keycolumns to one and two
ALTER TABLE one
ADD COLUMN new_id SERIAL NOT NULL UNIQUE
;
-- UPDATE:
-- This could need DEFERRABLE
-- Note since the update is only a permutation of the
-- existing values, we dont need to reset the sequence.
UPDATE one SET new_id = self.new_id
FROM ( SELECT id, row_number() OVER(ORDER BY id) AS new_id FROM one ) self
WHERE one.id = self.id;
ALTER TABLE two
ADD COLUMN new_fk INTEGER REFERENCES one(new_id)
;
-- update the new FK
UPDATE two t
SET new_fk = o.new_id
FROM one o
WHERE t.the_fk = o.id
;
SELECT * FROM two;
-- The crucial part: the final renaming
-- (at this point it would be better not to allow other sessions
-- messing with the {one,two} tables ...
-- --------------------------------------------------------------
ALTER TABLE one DROP COLUMN id CASCADE;
ALTER TABLE one rename COLUMN new_id TO id;
ALTER TABLE one ADD PRIMARY KEY(id);
ALTER TABLE two DROP COLUMN the_fk CASCADE;
ALTER TABLE two rename COLUMN new_fk TO the_fk;
CREATE INDEX ON two(the_fk);
-- Some checks.
-- (the automatically generated names for the indexes
-- and the sequence still contain the "new" names.)
SELECT * FROM two;
\d one
\d two
UPDATE: добавлена перестановка new_id (после ее создания как последовательного)
Смешная вещь: она не нуждается в "DEFERRABLE".
Ответ 4
* Этот script будет работать для postgresql
Это общее решение, которое работает для всех случаев
Этот запрос находит описание полей всех таблиц из любой базы данных.
WITH description_bd AS (select colum.schemaname,coalesce(table_name,relname) as table_name , column_name, ordinal_position, column_default, data_type, is_nullable, character_maximum_length, is_updatable,description from
( SELECT columns.table_schema as schemaname,columns.table_name, columns.column_name, columns.ordinal_position, columns.column_default, columns.data_type, columns.is_nullable, columns.character_maximum_length, columns.character_octet_length, columns.is_updatable, columns.udt_name
FROM information_schema.columns
) colum
full join (SELECT schemaname, relid, relname,objoid, objsubid, description
FROM pg_statio_all_tables ,pg_description where pg_statio_all_tables.relid= pg_description.objoid ) descre
on descre.relname = colum.table_name and descre.objsubid=colum.ordinal_position and descre.schemaname=colum.schemaname )
В этом запросе предлагается решение для исправления последовательности всех таблиц базы данных (это генерирует запрос в поле req, который фиксирует последовательность разных таблиц).
Он находит количество записей таблицы и затем увеличивает это число на единицу.
SELECT table_name, column_name, ordinal_position,column_default,
data_type, is_nullable, character_maximum_length, is_updatable,
description,'SELECT setval('''||schemaname||'.'|| replace(replace(column_default,'''::regclass)',''),'nextval(''','')||''', (select max( '||column_name ||')+1 from '|| table_name ||' ), true);' as req
FROM description_bd where column_default like '%nextva%'