Компактные или нумеруемые идентификаторы для всех таблиц и 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%'