Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 2004.03.03;
Скачать: [xml.tar.bz2];

Вниз

Получение значения из триггера по созданию новой записи   Найти похожие ветки 

 
Boroda Oleg   (2004-02-04 16:15) [0]

Добрый день!

Точно помню, что уже читал когда-то обсасывание этой проблеммы, но вот где - не помню.

Нужно получить ID записи после ее создания. В самой таблице организован метод автоматической генерации новых ключей через генератор. Естественно, имеется тригер, который, надо думать, срабатывает при создании новых записей. Как "увидеть" из-под Дельфи значение параметра ID после добавления новой записи?


 
Johnmen   (2004-02-04 16:17) [1]

GEN_ID(MyGenerator,0)
НЕ гарантируется достоверность при многопользовательской работе.


 
Boroda Oleg   (2004-02-04 16:23) [2]

Не, в триггере я ее прописал:
if (NEW.ID_ACCOUNT is NULL) then NEW.ID_ACCOUNT = gen_id(GEN_ACCOUNT,1);

Меня заботит проблемма, как этот NEW.ID_ACCOUNT увидеть из Дельфи, после того, как он стал не NULL.


 
Vlad   (2004-02-04 16:37) [3]

Refresh AfterPost ?


 
Johnmen   (2004-02-04 16:45) [4]

>Boroda Oleg

Если твоему приложению надо знать этот ID, то стандартная рекомендация - получать его значение ДО вставки.


 
Boroda Oleg   (2004-02-04 17:02) [5]

Т.Е. поднимаю значение генератора
Вызываю DataSet.Insert

Один недостаток - а если два юзера практически одновременно запустят новую запись?
Может проще не пользоваться тригерами, а вызывать StoredProc по DataSet.onNewRecord - у меня раньше все так было реализованно, сбоев не было не разу, правда работал через BDE.


 
HSolo   (2004-02-04 17:10) [6]

> а если два юзера практически одновременно запустят новую запись?

Ну и что? Вы же, надеюсь берете на клиенте
GEN_ID(MyGenerator,1)
а потом вставляете?


 
Johnmen   (2004-02-04 17:15) [7]

>Boroda Oleg (04.02.04 17:02)

Не вижу описание недостатка...:)


 
Boroda Oleg   (2004-02-04 17:23) [8]

Б-р-р-р. Что то не понял.

GEN_ID(MyGenerator,1) - вызывается триггером или это что-то из под самой Дельфи?

Тогда, если в дельфе строится приблизительно такой список процедур:
/* создаем новую запись */
получи_генератор;
DataSource.Insert;

И два юзера умудрились одновременно нажать пимпочку "создать"

Юзер1 - получает значение генератора (кстати, пока не знаю, как это сделать)
Юзер2 - получает значение генератора (то же что и Юзер1!!!)
Юзер1 - создает новую запись
Юзер2 - создает новую запись

Получается, что и Юзер1 и Юзер2 имеют одинаковый ID для Дельфи, а в базе они естественно построены правильно, и оба юзера начнут редактировать одну запись.


 
Johnmen   (2004-02-04 17:30) [9]

>получи_генератор;

Так с наращением получи :) gen_id(GEN_ACCOUNT,1);


 
Пубертанец   (2004-02-04 17:36) [10]

Если используешь провайдеры, то для соответствующего провайдера в опциях установи poAutoRefresh. Или после Post можешь вызывать DataSet.Refresh - и читай уже из датасета свой ИД.


 
Boroda Oleg   (2004-02-04 17:39) [11]

Так тот же смысл получается. Программа же не знает, что юзвери у нас занялись соревнованием по нажатию кнопки "create". Она добросовестно отдаст значение генератора, то которое имеет, а потом создаст две записи, у которых значения ID будут различаться ровно на единичку. Но! 2 юзверя уже имеют эти значения, и они равны. К примеру, оба юзверя успели получить значения 255, а база создала 255 и 256 - в таком случае юзвери добросовестно редактируют строку 255, а строка 256 скорее всего сбросится в базе так как не получит Post.


 
Boroda Oleg   (2004-02-04 17:42) [12]

2 Пубертанец
Прошу прощения, что такое провайдеры? И как определить, даже после refresh значение ID? Сотворить SQL запрос с определением максимального значения ID?


 
Johnmen   (2004-02-04 17:44) [13]

> Но! 2 юзверя уже имеют эти значения, и они равны

Что-то ты не догоняешь...:)
Если вызвали gen_id(GEN_ACCOUNT,1); то значение генератора наростилось и потом получено было. И следующий вызов, не зависимо от юзера, даст следующее значение...


 
Boroda Oleg   (2004-02-04 17:57) [14]

Да, но эти значения реально имеются в базе.
Меня же интересует вопрос о том, как их реально получить в Дельфе, причем в строгом соответствии с тем что в базе.

При вызове команды DataSet.Insert внутри БД отрабатывается триггер, который напрямую связан с генератором. Поле ID получает текущее значение генератора, после чего инкринируется на 1. Дельфи этого не видит никак. Она не знает, какое значение в данный момент хранится в генераторе.

Если я перед созданием записи в Дельфе получу значение генератора из Базы данных, а затем создам новую запись, у меня нет гарантии, что в то же самое время кто-то другой не попытается сделать то же самое, и если он получит значение генератора до того, как я создам запись, значения генераторов будут идентичными и у меня, и у него, так как мой триггер еще не вызывался.


 
Johnmen   (2004-02-04 18:03) [15]

Кажется понял твою проблему...

Убери из триггера. Получай в приложении, до вставки...


 
Boroda Oleg   (2004-02-04 18:09) [16]

Ок. Спасибо.
Буду эксперементировать.


 
Deniz   (2004-02-05 08:37) [17]

> Johnmen © & Boroda Oleg
Вы говорите об одном и том же, но в разных понятиях

Триггер НЕ ТРОГАТЬ.
В приложении, например перед постом или после вставки:

if MyIDField.IsNull then MyIDField.AsInteger:=GetNextID;
...
function GetNextID:Integer;
begin
Query1.Active:=False;
Query1.SQL.Text:=
"select gen_id(GEN_ACCOUNT, 1) from rdb$database";
Query1.Open;
Result:=Query1.Fields[0].AsInteger;
Query1.Close;
end;

И гарантия, что у каждого юзера(пусть даже 10 чел одновременно нажмут кнопку "добавить") будет СВОЙ ID.


 
ЮЮ   (2004-02-05 08:50) [18]

>Триггер НЕ ТРОГАТЬ.
>И гарантия, что у каждого юзера(пусть даже 10 чел одновременно нажмут кнопку "добавить") будет СВОЙ ID.

Тольго гарантии идут не столько от тригера, сколько от генератора!

Если даже 10 чел одновременно нажмут кнопку "взять новое значение генератора" (как в триггере) каждый получит СВОЁ


 
stud   (2004-02-05 09:23) [19]


> Триггер НЕ ТРОГАТЬ.
> В приложении, например перед постом или после вставки:

так вроде при таком раскладе получается:
1 получил значение из генератора
2 при вставке записи в таблицу сработал тригер и выдал новое значение. которое и запишется в таблицу.
либо с точностью до наоборот. но в любом случае полченное в приложении значение и записанное в таблицу будут разные.
так что триггер надо убирать


 
HSolo   (2004-02-05 09:46) [20]

>stud © (05.02.04 09:23) [19]
> 2 при вставке записи в таблицу сработал тригер и выдал новое значение

С какого перепугу? Если в триггере ясно сказано:

if (NEW.ID_ACCOUNT is NULL) then NEW.ID_ACCOUNT = gen_id(GEN_ACCOUNT,1);

А этот самый ID_ACCOUNT вовсе не NULL, а записано туда то самое значение генератора, к-рое получили перед вставкой


 
stud   (2004-02-05 09:51) [21]

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


 
Yuvich   (2004-02-06 14:29) [22]

А есть ли в синтаксисе insert у IB конструкции типа

insert into ... (id, data) values (0, :data) returning id into :id

где :id :data - параметры вставки.
Тогда вставляем только :data. id не знаем поэтому вставляем 0, а после срабатывания всех тригеров получаем :id.


 
Johnmen   (2004-02-06 14:34) [23]

Синтаксис можешь смело посмотреть здесь
Пуск-Программы-InterBase-Documentation-SQL Reference Help
:)))


 
Romkin   (2004-02-06 14:39) [24]

Deniz © (05.02.04 08:37) [17] и HSolo сказали все абсолютно правильно. А триггер остается на случай, если вставка в таблицу будет сделана не из данного приложения. Это просто гарантия, мало ли, может быть, например, другой разработчик будет работать с таблицей или еще что-нибудь...
2Yuvich нет такого, и не надо. Правда, вставлять можно просто
insert into ... (id, data) values (GEN_ID(MyGen,1), ...)


 
Yuvich   (2004-02-06 14:49) [25]

Обращение к генератору надо оставить или в приложении или в тригере, но в тригере - правильнее. Если нельзя получить значение поля после инсерта, то надо получать это значение до инсерта => обращение к генератору надо оставить в приложении.


 
Romkin   (2004-02-06 14:52) [26]

2Yuvich Почему так строго, или там, или там? У меня практически всегда и там, и там :))


 
MV   (2004-02-06 14:52) [27]

С такими триггерами (кот. анализируют Null) все одновалентно:

insert into ... (id, data) values (GEN_ID(MyGen,1), ...)
insert into ... (id, data) values (Null, ...)
insert into ... (data) values (...)

Имеет смысл получать очередной Id до поста. (Через генератор, GUID etc...):
Знание нового Id на клиенте хорошо тем, что можно компактный рефреш после инсерт/пост сделать. (...where id = :id) (см. компоненты TIBDataSet/IBX и TpFIBDataSet/FIB+)


 
Romkin   (2004-02-06 14:58) [28]

Знание ID сразу на клиенте в основном хорошо для связки мастер-деталь, не требуется постить на сервер мастера, чтобы узнать id, который в записи детали пихать надо. А потом просто в одной транзакции весть пакет пихаешь на сервер.


 
MV   (2004-02-06 15:00) [29]

2 Romkin © (06.02.04 14:58) [28]
Тоже ОК, хотя придется юзать кэширование изменений, наверное?


 
Yuvich   (2004-02-06 15:02) [30]

> Romkin

Представь: в приложении генератор возвращает, к примеру, 200. Далее идет инсерт и срабатывает тригер.
В тригере генератор возвращает уже 200 + 1 = 201. В таблицу пишется 201, а приложение "думает" что 200.

> MV: "Знание нового Id на клиенте хорошо тем, что можно компактный рефреш после инсерт/пост сделать ..."

Совершенно верно.

----
Неужели в IB нет стандартного механизма получения значения обновленных полей?


 
MV   (2004-02-06 15:05) [31]

Ну почему же!
Юзайте процедуры!


 
Johnmen   (2004-02-06 15:05) [32]

>В тригере генератор возвращает уже 200 + 1 = 201. В таблицу пишется 201,

Хм-м-м.. С чего бы ?


 
Romkin   (2004-02-06 15:08) [33]

2Yuvich Стандартный механизм - перечитать запись. Но этого и не требуется, просто запрашиваешь инкремент заранее, и все. Так же и в Oracle. Только MSSQL до сих пор почему-то держится за identity...
Насчет остального: ты строчку
if (NEW.ID_ACCOUNT is NULL) then NEW.ID_ACCOUNT = gen_id(GEN_ACCOUNT,1);
видел? Если у поля уже есть значение, ничего триггер делать не будет! Так что запишется 200

2MV А у меня все через кеширование, транзакции открываются только на момент выборки или внесения изменений :))


 
MV   (2004-02-06 15:10) [34]

Ну вы, блин, даете!

Посмотрите документацию:
компоненты IBX, компонент TIBDataSet,
свойства:
GeneratorField.Generator
GeneratorField.ApplyEvent


 
Romkin   (2004-02-06 15:12) [35]

ЛУчше универсально, по старинке :)


 
HSolo   (2004-02-06 15:15) [36]

> Yuvich (06.02.04 15:02) [30]
Опять 25 :))

> Представь: в приложении генератор возвращает, к примеру, 200. Далее идет инсерт и срабатывает тригер.
В тригере генератор возвращает уже 200 + 1 = 201. В таблицу пишется 201, а приложение "думает" что 200.

Еще раз. Медленно.
1. Приложение дернуло генератор. Получило 200
2. Приложение отправило на сервер insert, где значение ID выставлено в то самое 200 - а зачем, по-Вашему, приложение его получало?
3. На сервере включился триггер. Код триггера:
if (NEW.ID_ACCOUNT is NULL) then NEW.ID_ACCOUNT = gen_id(GEN_ACCOUNT,1);
Код начинает выполняться. Проверяется значение NEW.ID_ACCOUNT - оно равно 200. Значит, условие (NEW.ID_ACCOUNT is NULL) НЕ выполняется. И никто этот несчастный генератор НЕ дергает. И в таблицу вставится именно 200. Так что приложение будет думать совершенно правильно.
Ну, конечно, если это 200 по дороге марсиане унесут... тогда ой :))

А зачем такой триггер нужен, исчерпывающе объяснил Romkin © (06.02.04 14:39) [24]


 
Yuvich   (2004-02-06 15:15) [37]

> Johnmen
Как минимум 201. А разве нет? Ведь в тригере написано

... NEW.ID_ACCOUNT = gen_id(GEN_ACCOUNT,1);

> MV
Я ж говорю СТАНДАРТНЫЕ. Процедуры не должны буть нужны.


 
MV   (2004-02-06 15:16) [38]

2MV А у меня все через кеширование, транзакции открываются только на момент выборки или внесения изменений :))

Блин, я как-то не подумал о такой возможности, наверное, удобно по всяким там диал-ап соединениям работать... Сейчас грузят меня - типа даваешь интернет, а какой им интернет, если у меня сплошь и рядом Master/detail/subdetail... на локальную сеть оптимизированы...


 
Johnmen   (2004-02-06 15:18) [39]

>Yuvich (06.02.04 15:15) [37]
>... NEW.ID_ACCOUNT = gen_id(GEN_ACCOUNT,1);

Про ... уже разжевали :)


 
Yuvich   (2004-02-06 15:21) [40]

> HSolo
Прошу прощения. Невнимательно читал.

> Romkin
Вы правы.



Страницы: 1 2 вся ветка

Форум: "Базы";
Текущий архив: 2004.03.03;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.54 MB
Время: 0.069 c
3-5939
_юзер_
2004-02-06 16:39
2004.03.03
table xxx is in use


14-6190
Andreas
2004-02-11 18:29
2004.03.03
Пропали подключения ?


14-6209
Vinter
2004-02-11 15:33
2004.03.03
Инсталятор


14-6238
ККВ
2004-02-10 13:54
2004.03.03
Microsoft Help WorkShop


1-6105
Cerber
2004-02-20 19:35
2004.03.03
перевести фокус на нужное окно





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский