Консультация № 142456
28.08.2008, 07:50
0.00 руб.
0 15 0
Здравствуйте, уважаемые эксперты.

Delphi
FireBird 2

Есть таблица, первое поле которой: ID - autoincrement (триггер и генератор), подключаюсь к базе через dbExpress (TSimpleDataSet).

Пытаюсь добавить новую запись:

SimpleDataSet.Insert;
SimpleDataSet.FieldByName('str_field').AsString := 'Test';
SimpleDataSet.FieldByName('int_field').AsInteger := 1;
SimpleDataSet.Post;

Однако при попытке внести изменения (Post) выскакивает ошибка "Field 'ID' must have a value", но ведь сервер должен назначать новое значение автоинкремент-поля сам.

Что я не так делаю? Подскажите!!!

Обсуждение

давно
Мастер-Эксперт
425
4118
28.08.2008, 08:36
общий
Цитата: Минеев Андрей Анатольевич
... но ведь сервер должен назначать новое значение автоинкремент-поля сам.

Он это будет делать только при соблюдении двух условий:
1. Вы не забыли задать генератор.
2. Текст триггера вставляет сгенерированное значение в нужное поле.
По первому условию можно поверить Вам на слово, а вот по второму условию извините...
Текст триггера приведите пожалуйста.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
28.08.2008, 09:21
общий
AS
begin
if ((NEW.ID IS NULL) OR (NEW.ID = 0)) then
NEW.ID = gen_id(T_GEN_ID, 1);
давно
Мастер-Эксперт
425
4118
28.08.2008, 09:53
общий
Минеев Андрей Анатольевич

Нет, текст триггера полностью . Например:
CREATE TRIGGER ИмяТриггера FOR ИмяТаблицы
BEFORE или AFTER
DELETE или INSERT или UPDATE
AS
BEGIN
ТелоТриггера
END
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
28.08.2008, 10:01
общий
Триггер я создавал при помощи IBExpert (плохо знаю SQL), а уже в созданный триггер вставил код
В его настройках выставил срабатывание BEFORE при INSERT
давно
Мастер-Эксперт
425
4118
28.08.2008, 10:28
общий
Минеев Андрей Анатольевич
Хм... Ну что Вам тут сказать...
Работать автоинкремент будет только в одном случае - если Вы написали триггер вида BEFORE INSERT, т.е. триггер, который срабатывает до вставки новой записи.
Советы:
- не задавайте столбцу ID значение DEFAULT.
- не ставьте этому столбцу NOT NULL.
тогда проверку можно укоротить и, следовательно, ускорить:
IF NEW.id IS NULL
...
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
28.08.2008, 10:33
общий
Тут нашел статью http://www.ibase.ru/devinfo/generator.htm, там есть интересный момент

цитата:
Конечно, триггер можно оставить, изменив лишь код

IF (NEW.CLIENT_ID IS NULL) THEN
NEW.CLIENT_ID = GEN_ID(NEWID, 1);
чтобы если никакое значение столбца CLIENT_ID при вставке записи из приложения не передано, то оно было сгенерировано автоматически.

примечание: первая попытка перенести ответственность за автоматическую нумерацию столбца первичного ключа таблицы обычно проваливается из-за компонент доступа. Поскольку такой столбец объявлен как not null, и компоненты автоматически считывают характеристики столбцов, у TField будет установлено в True свойство Required. Это не дает возможности оставить столбец "пустым" при передаче с клиента на сервер. Установите свойство Required у такого столбца в False .
Конец цитаты.

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

Да, столбцу ID у меня присвоенно NOT NULL, значит нужно убрать?
давно
Мастер-Эксперт
425
4118
28.08.2008, 10:52
общий
Это относится не к серверу, а компоненту в Вашей программе.
Работает то что Вы написали так:
- если стоит Required:=True, т.е. значение в поле должно быть обязательно, то проверку обязательности проводит сначала сам компонент. Если значения нет, то данные на сервер для вставки вообще не отправляются. По этой причине нельзя смешивать политику проверки данных компонента с сервером. Лучше сделать это только на сервере, тогда Вы можете обращаться к базе из люой программы, а не только из той, что написали Вы и при этом будете уверены, что данные вставляются корректно.
Это утверждение верно, но, по-моему, должно появляться сообщение Дельфи, что столбец не должен оставаться пустым, а у Вас ведь этого нет.
Проверьте, правильно ли Вы написали триггер таким способом:
- зайдите в каталог, где у Вас установлен FireBird. Там в подкаталоге BIN есть программа isql.exe. Это командный интерпретатор SQL комманд.
- зайдя, выберите свою базу данных (SELECT DATABASE ИмяВашейБазыДанных;) и попытайтесь вставить новую строку в Вашу таблицу:
INSERT INTO ИмяТаблицы (tr_field) VALUES ('Test');
- теперь посмотрите что получилось:
SELECT * FROM ИмяТаблицы;
Если автоинкремент работает, то в выводе результатов запроса, рядом со словом 'Test' будет стоять тот номер, который вставил триггер. Если триггер написан неправильно, то номера никакого не будет или выскочит сообщение об ошибке при вставке.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
28.08.2008, 10:54
общий
Спасибо, вечером попробую и отпишусь.

p.s. Да вспомнил, я через IBExpert пробывал вставлять новую запись, все работало нормально, запись добавлялась и поле ID увеличивалось, значит триггер срабатывает правильно, возможно это все таки на уровне компонента TSimpleDataSet.
давно
Мастер-Эксперт
425
4118
28.08.2008, 10:57
общий
Цитата: Минеев Андрей Анатольевич
Да, столбцу ID у меня присвоенно NOT NULL, значит нужно убрать?

Ну, в принципе, можно и не убирать, т.к. значение генератора у Вас должно присваиваться до вставки записи. Но, согласитесь, если значения NULL всё равно там не будет, то излишне напрягать сервер, который должен проверять значение на NOT NULL. Другое дело, если Вы зачем-то будете редактировать уже готовый столбец ID (а делать этого не нужно, т.к. нарушиться связность данных между таблицами), тогда NOT NULL там нужен.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
28.08.2008, 11:05
общий
Да и еще, я как понял DbExpress работает с данными локально, пока не поступит команда ApplyUpdate, значит при выполнении команды Post ни чего никуда не отправляется и поэтому наверно ошибку выдает TSimpleDataSet.
давно
Мастер-Эксперт
425
4118
28.08.2008, 11:47
общий
Цитата: Минеев Андрей Анатольевич
...я как понял DbExpress работает с данными локально, пока не поступит команда ApplyUpdate, значит при выполнении команды Post ни чего никуда не отправляется и поэтому наверно ошибку выдает TSimpleDataSet.

Совершенно верно. TSimpleDataSet работает с локальным набором данных, полученным с сервера.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
28.08.2008, 15:53
общий
Спасибо, вечером попробую и отпишусь

Вот, что у меня получилось:
Через isql.exe у меня не получилось, поэтому пробывал через IBExpert.
Как я уже писал при добавлении через IBExpert все нормально добавляется, счетчик увеличивается.
Свойство Required поля "ID" (SimpleDataSet) поставил False - выскочила ошибка (Project Proba.exe raised exception class EDBClient with message 'Field value reguired.'),
затем в таблице убрал с поля "ID" NOT NULL, оставив cвойство Required поля "ID" (SimpleDataSet) - False, все заработало, записи стали нормально добавляться.
Что вы можете сказать по этому поводу? Можно так оставить или нет?

И еще вопрос, может в триггере оставить только строку NEW.CLIENT_ID = GEN_ID(NEWID, 1) вообще без проверок, опасно это иле нет (редактировать "ID" я не буду?
давно
Мастер-Эксперт
425
4118
28.08.2008, 18:30
общий
Что вы можете сказать по этому поводу? Можно так оставить или нет?

Вот как раз так и надо.
Смотрите, что получается. Если Required:=True, то наличие значения в поле ID проверяется Вашей программой ещё до того , как запись уйдёт на сервер для вставки в таблицу и если значения в поле ID нет, то именно ваша программа (а не сервер) выдаёт сообщение об ошибке.
Если Required:=False, а в свойтве поля ID на сервере стоит NOT NULL, то ошибку уже выдаст вам сервер, т.к. сервер тоже проверяет до вставки записи значение поля на NOT NULL.
...может в триггере оставить только строку NEW.CLIENT_ID = GEN_ID(NEWID, 1) вообще без проверок, опасно это иле нет...

Лучше не надо. Вполне возможно, что Вам понадобится в будущем изменить последовательность номеров в поле ID и Вы введёт значение поля вручную. Тогда проверка позволит избежать перекрытия вручную введённого значения генератором.
Впрочем, если ручного ввода значения не будет, то можно обойтись и без проверки. Т.е. вводить или не вводить проверку зависит от логики работы с данными. Самому серверу это абсолютно по барабану.
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Неизвестный
28.08.2008, 18:35
общий
Спасибо Вам огромное!!!
Вы очень мне помогли.
давно
Мастер-Эксперт
425
4118
28.08.2008, 18:39
общий
Пожалуйста!
Заходите, если что...
Об авторе:
Я только в одном глубоко убеждён - не надо иметь убеждений! :)
Форма ответа