Сжатие или перенумерация идентификаторов для всех таблиц и сброс последовательности до максимального (id)? - PullRequest
8 голосов
/ 01 августа 2011

После долгого запуска я получаю все больше и больше дыр в поле id.Идентификаторы некоторых таблиц - int32, и последовательность идентификаторов достигает своего максимального значения.Некоторые из исходных кодов Java доступны только для чтения, поэтому я не могу просто изменить тип столбца id с int32 на long, что может нарушить API.

Я бы хотел изменить их все.Это может быть не очень хорошая практика, но хорошо или плохо это не касается этого вопроса.Я хочу изменить нумерацию, особенно те очень длинные идентификаторы, как «61789238», «548273826529524324».Я не знаю, почему они такие длинные, но более короткие идентификаторы также легче обрабатывать вручную.

Но сжатие идентификаторов вручную нелегко из-за ссылок и ограничений.

Имеет ли PostgreSQLсама поддержка перенумерации ID?Или есть какой-нибудь плагин или поддерживающая утилита для этой работы?

Может быть, я смогу написать несколько хранимых процедур?Это было бы очень хорошо, поэтому я могу планировать это раз в год.

Ответы [ 4 ]

10 голосов
/ 24 августа 2015

Вопрос старый, но мы получили новый вопрос от отчаявшегося пользователя 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;

Это также *На 1051 * намного быстрее для больших таблиц, потому что проверка ограничений 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 .

10 голосов
/ 01 августа 2011

Предполагая, что ваши идентификаторы генерируются из последовательности 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)

Сбросить вашу последовательность:

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 голосов
/ 24 августа 2015

новый столбец id и внешний ключ (и), в то время как старые все еще используются. При некоторых (быстрых) переименованиях приложениям не нужно знать об этом. (Но приложения должны быть неактивными во время последнего шага переименования)

\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

ОБНОВЛЕНИЕ: добавлена ​​перестановка new_id (после создания его как последовательного) Забавно то, что, похоже, «DEFERRABLE» не требуется.

1 голос
/ 19 января 2016

* Этот скрипт будет работать для 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%' 
...