Исправление структуры таблицы во избежание `Ошибка: двойное значение ключа нарушает уникальное ограничение`

15

У меня есть таблица, которая создана таким образом:

--
-- Table: #__content
--
CREATE TABLE "jos_content" (
  "id" serial NOT NULL,
  "asset_id" bigint DEFAULT 0 NOT NULL,
   ...
  "xreference" varchar(50) DEFAULT '' NOT NULL,
  PRIMARY KEY ("id")
);

Позже некоторые строки вставляются с указанием идентификатора:

INSERT INTO "jos_content" VALUES (1,36,'About',...)

На более позднем этапе некоторые записи вставляются без идентификатора , и они завершаться с ошибкой: Error: duplicate key value violates unique constraint.

Видимо, идентификатор был определен как последовательность:

введите описание изображения здесь

Каждая неудачная вставка увеличивает указатель в последовательности до тех пор, пока он не увеличится до значения, которого больше не существует, и запросы будут выполнены успешно.

SELECT nextval('jos_content_id_seq'::regclass)

Что не так с определением таблицы? Какой умный способ исправить это?

Валентин Деспа
источник
В PostgreSQL вам не нужно заключать в кавычки имена столбцов и таблиц, если они все строчные.
Родриго

Ответы:

19

Нет ничего плохого в вашем определении таблицы.
(За исключением того, что я бы использовал jos_content_idили что-то вместо неописательного имени столбца id.
И я бы, вероятно, использовал textвместоvarchar(50) .

Ваше INSERTутверждение является проблемой.

Когда ваш idстолбец определен как serial, вы не должны вставлять ручные значения для id. Они могут сталкиваться со следующим значением из связанной последовательности.

Предоставьте явный список целевых столбцов (что почти всегда является хорошей идеей для постоянных INSERTоператоров) и полностью опустите последовательные столбцы .

INSERT INTO jos_content(asset_id, some_column, ...)
VALUES (36,'About',...);

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

INSERT ...
RETURNING id;  -- possibly more

Больше деталей в этом связанном ответе на SO:

Если у вас есть ручные записи в serialстолбцах, которые могут конфликтовать позже, установите вашу последовательность на текущий максимум, idчтобы исправить это один раз :

SELECT setval('jos_content_id_seq', max(id))
FROM   jos_content;

Где jos_content_id_seqимя по умолчанию для последовательности jos_content.id, которой вы владеете, которую вы уже нашли в столбце default. Кажется, xhzt8_content_id_seqв вашем случае;


Обновление: аналогичная проблема возникла в SO, и я нашел новое решение:

Эрвин Брандштеттер
источник
Разве текст не медленнее, чем varchar (50)?
Родриго
2
@ Родриго: Не в Postgres. Для более подробного объяснения есть ссылка выше: dba.stackexchange.com/a/21496/3684 . Или здесь. dba.stackexchange.com/a/89433/3684
Эрвин Брандштеттер,
Последний тест здесь < depesz.com/2010/03/02/charx-vs-varcharx-vs-varchar-vs-text > убедил меня в том, что varchar (n) быстрее для большинства полей, где удобно ограничение по размеру (люди имена, электронные письма, адреса, названия видов и т. д.). Кажется, текст быстрее (или такой же), если вы не будете проверять длину.
Родриго