Как заполнить «дыры» в полях автоинкремента? - PullRequest
16 голосов
/ 03 декабря 2009

Я прочитал несколько постов по этому поводу, но ни один не освещает эту проблему.

Полагаю, это невозможно, но я все равно спрошу.

У меня есть таблица с более чем 50 000 регистров. Это старая таблица, в которой выполнялись различные операции вставки / удаления.

Тем не менее, существуют различные «дыры» в некоторых из 300 регистров. Т.е.: ..., 1340, 1341, 1660, 1661, 1662, ...

Вопрос в том. Есть ли простой / легкий способ заставить новые вставки заполнить эти «дыры»?

ТНХ Пауло Буэно

Ответы [ 7 ]

13 голосов
/ 03 декабря 2009

Я согласен с @Aaron Digulla и @Shane N. Пробелы не имеют смысла. Если они DO что-то значат, это неверный дизайн базы данных. Период.

При этом, если вам абсолютно НЕОБХОДИМО заполнить эти дыры, И вы используете по крайней мере MySQL 3.23, вы можете использовать ВРЕМЕННЫЙ СТОЛ для создания нового набора идентификаторов. Идея заключается в том, что вы собираетесь выбрать все свои текущие идентификаторы по порядку во временной таблице как таковой:

CREATE TEMPORARY TABLE NewIDs
(
    NewID INT UNSIGNED AUTO INCREMENT,
    OldID INT UNSIGNED
)

INSERT INTO NewIDs (OldId)
SELECT
    Id
FROM
    OldTable
ORDER BY
    Id ASC

Это даст вам таблицу, отображающую ваш старый Id на новый Id, который будет иметь последовательный характер, благодаря свойству AUTO INCREMENT столбца NewId.

Как только это будет сделано, вам нужно обновить любую другую ссылку на Id в «OldTable» и любой внешний ключ, который он использует. Для этого вам, вероятно, потребуется сбросить все имеющиеся у вас ограничения внешнего ключа, обновить любую ссылку в таблицах с OldId на NewId, а затем заново установить ограничения внешнего ключа.

Однако я бы сказал, что вы не должны делать ЛЮБОГО этого, а просто понимать, что ваше поле Id существует исключительно для ссылки на запись и должно иметь НЕ любое конкретное отношение.

ОБНОВЛЕНИЕ: добавление примера обновления идентификаторов

Например:

Допустим, у вас есть следующие 2 схемы таблиц:

CREATE TABLE Parent
(
    ParentId INT UNSIGNED AUTO INCREMENT,
    Value INT UNSIGNED,
    PRIMARY KEY (ParentId)
)

CREATE TABLE Child
(
    ChildId INT UNSIGNED AUTO INCREMENT,
    ParentId INT UNSIGNED,
    PRIMARY KEY(ChildId),
    FOREIGN KEY(ParentId) REFERENCES Parent(ParentId)
)

Теперь, пробелы появляются в вашей родительской таблице.

Чтобы обновить ваши значения в Parent и Child, вы сначала создаете временную таблицу с отображениями:

CREATE TEMPORARY TABLE NewIDs
(
    Id INT UNSIGNED AUTO INCREMENT,
    ParentID INT UNSIGNED
)

INSERT INTO NewIDs (ParentId)
SELECT
    ParentId
FROM
    Parent
ORDER BY
    ParentId ASC

Далее, мы должны указать MySQL игнорировать ограничение внешнего ключа, чтобы мы могли правильно ОБНОВИТЬ наши значения. Мы будем использовать этот синтаксис:

SET foreign_key_checks = 0;

Это заставляет MySQL игнорировать проверки внешнего ключа при обновлении значений, но все равно будет принудительно использовать правильный тип значения (подробности см. Справочник по MySQL ).

Далее нам нужно обновить наши родительские и дочерние таблицы новыми значениями. Для этого мы будем использовать следующий оператор UPDATE:

UPDATE
    Parent,
    Child,
    NewIds
SET
    Parent.ParentId = NewIds.Id,
    Child.ParentId = NewIds.Id
WHERE
    Parent.ParentId = NewIds.ParentId AND
    Child.ParentId = NewIds.ParentId

Теперь мы корректно обновили все наши значения ParentId до новых упорядоченных идентификаторов из нашей временной таблицы. Как только это будет завершено, мы сможем восстановить наши проверки внешнего ключа для сохранения ссылочной целостности:

SET foreign_key_checks = 1;

Наконец, мы удалим нашу временную таблицу для очистки ресурсов:

DROP TABLE NewIds

И это все.

8 голосов
/ 03 декабря 2009

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

5 голосов
/ 03 декабря 2009

Вам вообще не нужно заботиться о пробелах. Если вы подходите к концу типа данных для идентификатора, то относительно легко изменить таблицу, чтобы перейти к следующему по величине типу int.

Если вам абсолютно необходимо начать заполнять пробелы, вот запрос, чтобы вернуть наименьший доступный идентификатор (надеюсь, не слишком медленно):

SELECT MIN(table0.id)+1 AS newid
FROM table AS table0
LEFT JOIN table AS table1 ON table1.id=table0.id+1
WHERE table1.id IS NULL

(не забудьте использовать транзакцию и / или перехватывать вставки дубликатов ключей, если вам нужны параллельные вставки для работы.)

2 голосов
/ 11 ноября 2011
INSERT INTO prueba(id) 
VALUES (
(SELECT IFNULL( MAX( id ) , 0 )+1 FROM prueba target))

IFNULL для пропуска нуля при нулевом количестве строк

добавить цель для пропуска ошибки mysql "error error FROM)

1 голос
/ 03 декабря 2009

Существует простой способ, но он неэффективен: просто попробуйте вставить с идентификатором, а когда это не удастся, попробуйте следующий.

В качестве альтернативы, выберите идентификатор, а когда вы не получите результат, используйте его.

Если вы ищете способ заставить БД автоматически заполнять пробелы, то это невозможно. Более того, это никогда не должно быть необходимым. Если вы чувствуете, что вам это нужно, то вы злоупотребляете внутренним техническим ключом для чего-то, кроме одной единственной цели: позволить вам объединять таблицы.

[РЕДАКТИРОВАТЬ] Если это не первичный ключ, вы можете использовать этот оператор обновления:

update (
    select *
    from table
    order by reg_id -- this makes sure that the order stays the same
)
set reg_id = x.nextval

где x - новая последовательность, которую вы должны создать. Это перенумерует все существующие элементы, сохраняя порядок. Это не удастся, если у вас есть ограничения внешнего ключа. И это повредит вашу базу данных, если вы будете ссылаться на эти идентификаторы в любом месте без ограничений внешнего ключа.

Обратите внимание, что при следующей вставке база данных создаст огромный пробел, если вы не сбросите столбец идентификаторов.

0 голосов
/ 05 декабря 2017

Как уже говорили другие, это не имеет значения, и если это так, то что-то не так в вашей структуре базы данных. Но лично мне все равно, чтобы они были в порядке!

Вот несколько SQL, которые воссоздают ваши идентификаторы в том же порядке, но без пробелов.

Сначала это делается в поле temp_id (которое вам нужно будет создать), чтобы вы могли убедиться, что все хорошо перед тем, как перезаписать ваши старые идентификаторы. Замените Tbl и id, если необходимо.

SELECT @i:=0;
UPDATE Tbl
JOIN
(
    SELECT id
    FROM Tbl
    ORDER BY id
) t2
ON Tbl.id = t2.id
SET temp_id = @i:=@i+1;

Теперь у вас будет поле temp_id со всеми вашими новыми блестящими идентификаторами. Вы можете заставить их жить просто:

UPDATE Tbl SET id = temp_id;

А затем отбросив столбец temp_id.

Должен признать, я не совсем уверен, почему это работает, поскольку я ожидал, что движок будет жаловаться на дубликаты идентификаторов, но этого не произошло, когда я его запустил.

0 голосов
/ 02 июля 2010

Возможно, вы захотите убрать пробелы в приоритетном столбце. Путь ниже даст поле автоинкремента для приоритета. Дополнительное левое объединение на той же табеле обеспечит добавление в том же порядке, что и (в данном случае) приоритет

SET @a:=0;
REPLACE INTO footable
 (id,priority)
    (
    SELECT tbl2.id, @a 
    FROM footable as tbl
    LEFT JOIN footable as tbl2 ON tbl2.id = tbl.id  
    WHERE (select @a:=@a+1)
    ORDER BY tbl.priority
)
...