Серийный PostgreSQL (автоинкремент) с UniDac на Delphi XE2

Я перевожу наше приложение Delphi XE2 из MSSQL (с использованием компонентов ADO) в PostgreSQL, используя UniDAC.

В базе данных есть несколько serial поля типа (автоинкременты). Когда я добавляю запись, я не помещаю никаких данных в это поле автоинкремента. Раньше с MSSQL/ADO он работал автоматически, но теперь у меня есть исключение.

Код:

aqrMsgs.Append;
aqrMsgsUser_From.AsInteger := UserId;
aqrMsgsUser_To.AsString := UserIds[I];
aqrMsgsSubject.AsString := Trim (edtSubject.Text);
aqrMsgsContents.AsString := mmoContents.Text;
aqrMsgsIsDone.AsBoolean := False;
aqrMsgs.Post;

И исключение:

исключение

Поле id является TIntegerField, а не TAutoIncrementField.

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

Как можно это решить? Благодарю.

2 ответа

Решение

1) Когда вы создаете поле с серийным типом, сервер PostgreSQL автоматически создает последовательность, и значения из этой последовательности будут использоваться по умолчанию для этого поля. Если последовательное поле не установлено при вставке новой записи, сервер принимает значение из последовательности для него. Но если вы установите значение для последовательного поля, сервер вставит это значение. Поскольку последовательность ничего не знает о значении, которое вы вставили в последовательное поле, то при дальнейшей вставке записей (при использовании последовательности) может возникнуть ошибка "Значение дублированного ключа", если последовательное поле создано с уникальным ограничением., Вы не столкнетесь с этой проблемой, если не установите значения для этого поля вручную.

2) UniDAC может автоматически заполнять поля, используя последовательность. Для этого вы должны установить свойства TUniQuery.KeyFields и TUniQuery.SpecificOptions.Values ​​['KeySequence']] следующим образом:

  UniQuery1.KeyFields := 'id'; 
  UniQuery1.SpecificOptions.Values['KeySequence'] := 'test1_id_seq';

Кроме того, используя свойство TUniQuery.SpecificOptions.Values ​​['SequenceMode'], вы можете указать, когда именно UniDAC будет заполнять поле, используя последовательность: при вызове Append/Insert или Post.

Вы можете найти подробную информацию об упомянутых свойствах здесь: http://www.devart.com/unidac/docs/pgsqlprov_article.htm

Наиболее совместимым с SQL способом было бы вообще не включать это поле в SQL-запрос и оставлять его NULL а затем заполните его на сервере через SQL Before Insert trigger от null к уникальной ценности.

Это можно сделать, если UniDAC имеет настройку запроса вставки, как TUpdateSQL было.

Указание вручную INSERT запрос (или INSERT-RETURNING если вам понадобится идентификатор позже по каким-либо причинам: http://en.wikipedia.org/wiki/SQL_INSERT) - это наиболее контролируемый, эффективный и гибкий способ вставки данных.


Однако если вы делаете Append и не хочет писать SQL-запросы для этого, тогда вы можете прочитать значение идентификатора с сервера, прежде чем делать post,

1) объявить источник кросс-транзакционного идентификатора: SQL SEQUENCE

2) запрос nextval() перед тем, как делать пост и поместить его в поле идентификатора (вы можете сделать это в TDataSet.BeforePost обработчик события)


Вы также можете прочитать документацию UniDAC и увидеть несколько связанных с последовательностью свойств в TUniTable которые автоматизируют этот процесс для вас. KeySequence а также SequenceMode по адресу http://www.devart.com/unidac/docs/pgsqlprov_article.htm

Другие вопросы по тегам