Текущий архив: 2005.10.30;
Скачать: CL | DM;
ВнизВнешние ключи в контекст транзации. Найти похожие ветки
← →
Девушка © (2005-09-18 23:03) [0]Хочу в одной транзации добавить сначала добавить запись в родительскую таблицу (справочник), потом, без commit-а добавить запись в дочернюю таблицу в которой в связанном посредством внешнего ключа поле ссылается на еще не подтвержденную родительскую запись.
Выдается ошибка Foreign Key.
Транзакция настроена на ReadCommited.
Добавление записей происходит из DBGridEH, а справочники прописаны как LookUp-поля.
Как добавить запись?
Пример:
Есть таблица товаров и таблица приходов.
1. Стартанули транзакцию.
2. Добавили новый товар "Конфеты".
3. Добавили новый приход с товаром "Конфеты" (здесь возникает ошибка)
4. Либо подтвердили, либо откатили всю транзацию.
← →
Desdechado © (2005-09-18 23:04) [1]видимо, для новой записи справочника на клиент не получен код, необходимый для занесения в дочернюю таблцу
← →
Девушка © (2005-09-18 23:11) [2]
> видимо, для новой записи справочника на клиент не получен
> код, необходимый для занесения в дочернюю таблцу
Код получен. При трассировке в поле датасета дочерней таблице он виден и выбирается правильно.
← →
Девушка © (2005-09-18 23:12) [3]Код - в смысле, id-к
← →
Ильш © (2005-09-19 06:11) [4]в поле датасета виден
а на серваке его еще нет т.к. транзакция еще открыта
← →
Ярослав (2005-09-19 07:41) [5]На серваке он есть, даже если транзакция не поттверждена!
Обе таблици, главная и подчиненная, должны использовать одну транзакцию
← →
Johnmen © (2005-09-19 09:29) [6]>посредством внешнего ключа поле ссылается на еще не подтвержденную родительскую запись.
Это (подтвержденная или нет) не имеет значения.
Ошибка - неверный ссылочный ключ при новом приходе. Другого быть не может.
← →
Sergey13 © (2005-09-19 09:29) [7]2Девушка © (18.09.05 23:03)
Как то у тебя все неправильно. 8-)
1.Если где то между 2 и 4 пунктами узер уйдет чай пить то транзакция будет висеть неограниченно долго. А она и так у тебя очень долгая.
2.Получать ИДшник родителя можно (и лучше) ДО вставки.
3.Лукап поля на здоровые справочники (а справочник товаров наверное немаленький) не есть хорошо, ИМХО.
← →
msguns © (2005-09-19 10:14) [8]Чтоб не было ситуации, описанной в [7] (долгая пишущая транзакция), надо разделять чтение и запись. Т.е. запись в справочник и добавление к нему в дочернюю таблицу данных о движении (приходе) должны выполнять в отдельной записывающей транзакции.
Делается это так:
1. На клиенте получаем обе записи (мастер и детал, деталов может быть несколько, например, если работаем через стрингрид или CDS)
2. Стартуем на сервере новую пишущую транзакцию
3. Определяем новый ID товара, "дернув" соотв. генератор
4. Записываем в справочник, используя явно полученный ID (п.3)
5. Записываем в детал, используя в качестве ключа связки тот же ID (п.3)
6. Подтверждаем транзакцию
7. Переоткрываем основные НД и заполняем (если есть) визуализирующие контролы (стрингрид, CDS,..)
8. Позиционируем осн. таблицу на ID (п.3), активируя таким образом только что добавленный товар.
ЗЫ. Из сабжа сделал вывод о том, что БД учета товаров построена по-любительски, без отражения в ней реальных объектов-документов.
ЗЫ2. Нельзя жестко связывать добавление в справочники с добавлением в БД оперативной информации. Можно серьезно оконфузиться ;)))
← →
ANB © (2005-09-19 10:21) [9]Присоединяюсь к мнению msguns и вопрос автору сабжа :
А зачем весь огород в этой задаче ? Справочник товаров - штука более менее постоянная, туда новый товар нужно добавлять ручками пользователя и уж никак не локапом выбирать (т.к. потом придет идея построить дерево и т.п.)
Другой вопрос, если ты ведешь учет остатков и вот тогда изменения в таблице остатков должны согласовываться с добавлением в таблицу проводок и/или строк документов. Как это сделать - написал msguns.
← →
Девушка © (2005-09-19 11:17) [10]Справочник товаров был приведен лишь для примера т.к. является классикой. Реальная задача другая.
> Обе таблици, главная и подчиненная, должны использовать
> одну транзакцию
Так оно и есть. ReadCommeted
> msguns © (19.09.05 10:14) [8]
Если писать с помощью другой транзакции, транзакцию с которой связан датасет дочерней таблицы нужно будет изменить? Хотелось бы добавления средствами грида, а не ХП или SQL комманд.
← →
Sergey13 © (2005-09-19 11:20) [11]2[10] Девушка © (19.09.05 11:17)
>Хотелось бы добавления средствами грида, а не ХП или SQL комманд.
Это невозможно.
← →
Девушка © (2005-09-19 12:06) [12]
> Это невозможно.
В смысле не явно посылать команды а работать через IBDataset.Insert IBDataset.post и т.п.
← →
Андрей Жук © (2005-09-19 12:07) [13]Можешь использовать ХП
← →
Sergey13 © (2005-09-19 12:15) [14]2[12] Девушка © (19.09.05 12:06)
Я такие вещи иногда делаю так.
На отдельной форме кидаем нужные Едиты и грид если нужно. Грид призываю к отдельному датасету (я "люблю" RxMemoryData) с нужной структурой. При нажатии на "сохранить" делаю все как в [8], но через основные датасеты. И волки целы и овцы сыты.
Можно делать и так как ты. Но комитить каждое действие. Если по логике работы это допустимо, то в принципе ничего страшного.
← →
msguns © (2005-09-19 12:48) [15]Если речь идет о добавлении нового документа с двумя или более уровнями (фактурами), то имхо, следует разделять заголовок и "дочки".
Т.е. сначала юзер добавляет заголовок, он фиксируется в БД (короткая пишущая транзакция) и потом можно добавлять записи-дочки, подтвержение которых можно делать отдельными транзакциями либо "скопом" опять-же короткими пишущими транзакциями.
Какой путь выбрать - Вам виднее. Я бы советовал итти позаписным путем применительно к складу. Правда при этом придется ввести понятие "проведенный-непроведенный) документ, но это только добавить надежности и достоверности Вашим данным.
Какими копонентами работать - Вам виднее. Можно редактировать через грид (многие считают этот способ кульно-рульным), а можно использовать модальные формы с подтверждением вставки-изменения каждой записи.
Однако надо помнить, что объект TIBDataset сам делает подтверждение - откат транзакций (что требует от клиента доп.затрат на перехват разных BeforePost+AfterPost во избежании "фокусов" при "гуляющей" коррекции в гриде) и не дает такое 100 процентно контролируемое управление со стороны клиента, как пара TIBQuery+TIBSQL
← →
Девушка © (2005-09-19 14:18) [16]
> Если речь идет о добавлении нового документа с двумя или
> более уровнями (фактурами), то имхо, следует разделять заголовок
> и "дочки".
OK. Если разделять, то достаточно ли будет прописать commit в AfterPost датасета?
← →
msguns © (2005-09-19 14:29) [17]А внимательно и вдумчиво прочитать 2-й абзац [15] ?
← →
Девушка © (2005-09-19 14:47) [18]
> msguns © (19.09.05 14:29) [17]
Не совсем понятно, когда dataset сам откатывает/подтверждает транзакции.
А по поводу TIBQuery+TIBSQL дык это уже делалось... хочется новое освоить :)
← →
Sergey13 © (2005-09-19 14:51) [19]2[16] Девушка © (19.09.05 14:18)
Вместо commit используй CommitRetaining. Просто Commit закрывает транзакцию.
← →
Девушка © (2005-09-19 16:42) [20]
> Вместо commit используй CommitRetaining.
Этого достаточно ? :)
← →
Sergey13 © (2005-09-19 16:43) [21]2[20] Девушка © (19.09.05 16:42)
>Этого достаточно ? :)
Для чего?
← →
msguns © (2005-09-19 16:47) [22]>Девушка © (19.09.05 16:42) [20]
>Этого достаточно ? :)
Нет, если Вы хотите, чтобы добавленная (измененная) запись встала на "свое" место в гриде (в порядке сортировки).
Да, если Вы хотите, чтобы добавленную (измененную) запись увидели другие пользователи (при переоткрытии своих запросов разумеется)
← →
Sergey13 © (2005-09-19 16:51) [23]2 [22] msguns © (19.09.05 16:47)
Если через TIBDataset делать, то вроде должна встать куда надо.
← →
Девушка © (2005-09-19 20:07) [24]Что-то не получается...
Итак в родительском датасете в AfterPost стоит
transaction.CommitRetaining;
Добавляем новую запись в родительский датасет. Говорим post. Новая запись появилась в выпадающем списке DBGridEH дочернего датасета (т.к. lookup поле). Выбираем в этом выпадающем списке ту новую родительскую запись. При попытке post-ть получаем ошибку внешнего ключа.
Что нужно сделать что-бы внешний ключ "увидел" новую запись в родительской таблице?
Оба датасета завязаны на одну транзакцию.
Параметры транзакцииread_committed
rec_version
nowait
← →
Девушка © (2005-09-19 20:11) [25]Опытным путем выяснилось, что если в AfterPost кроме коммита родительский датасет сначала закрыть потом открыть (простой рефреш почему-то не сработал) то указанная ошибка не возникает.
← →
Девушка © (2005-09-19 20:34) [26]Но это - плохое решение т.к. приходится потом опять запись позиционировать. :(
← →
msguns © (2005-09-20 09:16) [27]Это нормальное решение для любой SQL-технологии
Refresh не перечитывает записи с сервера
← →
Sergey13 © (2005-09-20 09:35) [28]2[26] Девушка © (19.09.05 20:34)
Я тебе еще в [7] писал
>2.Получать ИДшник родителя можно (и лучше) ДО вставки.
Сделай так и вставляй его явно, а не получай при вставке тригером.
← →
Девушка © (2005-09-20 09:59) [29]
> >2.Получать ИДшник родителя можно (и лучше) ДО вставки.
> Сделай так и вставляй его явно, а не получай при вставке
> тригером.
id-к получается через GeneratorField и Gen_ID в InsertSQL.
> Это нормальное решение для любой SQL-технологии
Почему для того что-бы вставить запись в датасет дочерней таблицы, приходится перчитывать родительскую? Нелогично...
← →
Sergey13 © (2005-09-20 10:11) [30]2[29] Девушка © (20.09.05 09:59)
ЕПРСТ. 8-)
Получи его ДО вставки
Select Gen_Id(gen_name,1) from RDB$DATABASE
В тригере поставь проверку на НУЛЛ
if new.id is null then new.id=Gen_Id(gen_name,1)
Получи ИД-ник заранее и потом вставляй явно.
← →
msguns © (2005-09-20 10:27) [31]>Девушка © (20.09.05 09:59) [29]
>Почему для того что-бы вставить запись в датасет дочерней таблицы, приходится перчитывать родительскую? Нелогично...
Чтобы вставить дочернюю таблицу новую запись с ID только что вставленной родительской записи на сервере, ничего перечитывать не надо.
А вот как работает FIBDataSet, точнее что и когда он там перечитывает - фиг его знает, однако что-то он делает "не так", потому и вылазит эксепшн.
Я сам им не пользуюсь именно потому, что не уверен на 100%, что он правильно "поймет", что я хочу. И приходится больше приноравливаться к его поведению, чем решать собственно стоящие задачи.
Ясно, что у Вас при связке Мастер-детал он при добавлении в гл.датасет что-то недоруливает.
Вот почему я и говорил, что для явного управления транзакциями лучше использовать TIBQuery.
Кстати, проверьте опции коннекта. Там не должно быть SnapShot. И сколько у вас используется объектов транзакций ?
← →
Девушка © (2005-09-20 11:23) [32]
> Кстати, проверьте опции коннекта. Там не должно быть SnapShot.
> И сколько у вас используется объектов транзакций ?
SnapShot-а нет. Транзакция одна
read_committed
rec_version
nowait
> Получи ИД-ник заранее и потом вставляй явно.
Как вставлять явно через DNGridEH?
← →
Sergey13 © (2005-09-20 11:29) [33]> Как вставлять явно через DNGridEH?
Опять вставлять через грид! 8-)
В InsertSQL
insrte into table_name(ID,......) values (:ID,....)
В BeforePost закататй в ИД-шник то что получишь от
Select Gen_Id(gen_name,1) from RDB$DATABASE
И тригер проверь. Должно быть условие на НУЛЛ.
← →
Девушка © (2005-09-20 12:01) [34]
> Sergey13 © (20.09.05 11:29) [33]
Вопрос может быть глупый, но как этот прием решит сабжевую проблему? Ну добавлю запись с ID-м который будет сгенерирован явно... это мне даст лишь возможность locat-ть заново запись. Как сделать так, что-бы не пришлось переоткрывать родительский набор данных?
← →
Sergey13 © (2005-09-20 12:08) [35]2[34] Девушка © (20.09.05 12:01)
>Как сделать так, что-бы не пришлось переоткрывать родительский набор данных?
А зачем его потом переоткрывать если он будет соответствовать действительности? Ты же этот родительский ИДшник можешь в дочку писать сразу.
← →
Johnmen © (2005-09-20 12:27) [36]>id-к получается через GeneratorField и Gen_ID в InsertSQL.
Кстати, это и есть запрос на следующий id Sergey13 © (20.09.05 10:11) [30], просто неявный.
И вообще, что-то мусолится, мусолится одно и то же...:)
Страницы: 1 вся ветка
Текущий архив: 2005.10.30;
Скачать: CL | DM;
Память: 0.55 MB
Время: 0.04 c