Моим непосредственным решением этой проблемы, которая облегчила ситуацию вне диапазона, было изменение столбца на BIGINT, как показано ниже:
ALTER TABLE MyTable ALTER COLUMN idMyTable TYPE BIGINT;
Количество записей в моем случае было чрезвычайно маленьким (<1000), так что это было тривиальное изменение для выполнения. </p>
Как только это закончилось, пришло время искать решение основной проблемы. Мое решение вряд ли будет таким же эффективным, как использование поля SERIAL, поэтому имейте это в виду, исходя из вашего варианта использования, если вы собираетесь реализовать что-то похожее на то, что я сделал - всегда где-то есть компромисс.
Рассмотрим следующую таблицу и результирующую вставку данных / запрос:
CREATE TABLE TestTable ( id SERIAL PRIMARY KEY NOT NULL, Key TEXT UNIQUE NOT NULL, Val TEXT );
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'banana') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'apple') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'peach') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Animal', 'horse') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
SELECT * FROM TestTable;
id Key Val
1 Fruit peach
4 Animal horse
В этом случае каждый конфликт во время обновления Fruit повышал значение SERIAL, даже если он не создавал новую запись в TestTable.
Теперь это обходной путь, с которым я сейчас работаю. Если кто-нибудь знает, как объединить имя таблицы в 'NEW.id', я хотел бы услышать это, поскольку мне нравится называть мои столбцы идентификаторов idTablename для согласованности.
CREATE OR REPLACE FUNCTION IncrementSerial()
RETURNS trigger AS $fn$
BEGIN
EXECUTE format('SELECT COALESCE( MAX( id ), 0 ) + 1 FROM %I.%I;',TG_TABLE_SCHEMA,TG_TABLE_NAME) INTO NEW.id;
RETURN NEW;
END
$fn$ LANGUAGE 'plpgsql'
CREATE TABLE TestTable ( id INTEGER PRIMARY KEY NOT NULL, Key TEXT UNIQUE NOT NULL, Val TEXT );
CREATE TRIGGER trgIncrementSerial
BEFORE INSERT ON TestTable
FOR EACH ROW
EXECUTE PROCEDURE IncrementSerial()
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'banana') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'apple') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'peach') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Animal', 'horse') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
SELECT * FROM TestTable;
id Key Val
1 Fruit peach
2 Animal horse
Как вы можете видеть, SERIAL id теперь просто использует следующий наибольший номер id, что идеально подходит для большинства моих случаев использования.
Очевидно, что это будет проблемой, если ключ id всегда должен быть уникальным, так как удаление последней записи освободит этот идентификатор. Если это не проблема (т. Е. Где идентификатор используется только для ссылок и каскадов), то это может быть хорошим решением для вас.