Серийный 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
- http://doc.artofweb.ru/doc/ Последовательности_в_PostgreSQL
- http://www.neilconway.org/docs/sequences/
- http://www.postgresql.org/docs/9.1/static/functions-sequence.html
- http://www.postgresql.org/docs/8.1/static/sql-createsequence.html
2) запрос nextval()
перед тем, как делать пост и поместить его в поле идентификатора (вы можете сделать это в TDataSet.BeforePost
обработчик события)
Вы также можете прочитать документацию UniDAC и увидеть несколько связанных с последовательностью свойств в TUniTable
которые автоматизируют этот процесс для вас. KeySequence
а также SequenceMode
по адресу http://www.devart.com/unidac/docs/pgsqlprov_article.htm