:: Visual Foxpro, Foxpro for DOS
Как работать с Trigger-ами?
ZenTigra
Автор

Сообщений: 514
Дата регистрации: 03.12.2004
В свое время форумчане помогли мне избавиться от ON KEY LABEL , а также помогли разобраться с буферизацией.
Много пришлось переделать в программе, но я довольный результатом, большое Вам за это спасибо.

Теперь хочу разобраться с Триггерами.

Опишу маленьким примером ту задачу, которую хочу решить (до примера прошу не придираться, все очень упрощенно).

Есть две таблицы
table1 и table2

Структура table1
key - поле ключа
suma - сумма по полям table2

Структура table2
key - поле ключа для связи с table1
suma2 – любые цифры

Необходимо чтобы при изменении поля suma2 в table2 происходило автоматическое изменения значения поля suma в table1

Вот на подобии, этого.
table1
key suma
a1 100
a2 200


table2
key suma2
a1 50
a1 20
a1 30
a2 200


Сейчас вся логика обработки находиться в программе, но иногда случаются потеря целостности данных, когда поле suma не соответствует полю suma2. Думаю что это происходит из-за того, что поле suma2 изменяться из многих мест программы, и есть вероятность ошибки в коде. Вот я и хочу собрать этот код в одном месте (в хранимой процедуре БД), чтоб избавиться от этих ошибок.

Вопрос в логике, как это правильно делать.
Думаю, в форме где редактируется поле table2 при нажатии на кнопку «записать изменения» сделать код блокировки поля в table1, при успешной блокировке, произойдет сохранения записи, и автоматически выполниться триггер. Вот только хочется обойти подводные камни этой процедуры.

Еще вопрос, не относящейся к triggers, но относящийся к этой задаче.
Как правильней суммировать поле suma, брать предыдущее значение (в table1) и добавлять (отнимать) новое, или каждый раз суммировать поле suma2 в table2. Записи в table2 очень часто добавляются (изменяются) (приблизительно 500-1000 записей в день).
Количество записей в table2 ~300000

PS.В вдогонку, еще одна проблемка. Имя таблицы в программе не совпадает с алиасом. Как с этим работать в хранимой процедуре, обращаться к таблице нужно по ее имени или по алиасу.



Исправлено 3 раз(а). Последнее : ZenTigra, 08.04.20 09:15
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
ZenTigra
Автор

Сообщений: 514
Дата регистрации: 03.12.2004
Еще одно проблема.
Поле suma - не должно быть нулевым, и если пользователь набрал a1 -120 (то есть поле suma будет -20) то как отменить сохранение запись? Как реализуется защита при СОХРАНЕНИИ на ввод неправильных данных. (Пожалуйста не советуйте делать проверку до момента сохранения, я хочу еще в хранимую процедуру засунуть проверку на ошибки ввода, или в триггерах это делать НЕЛЬЗЯ)



Исправлено 2 раз(а). Последнее : ZenTigra, 08.04.20 09:26
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
of63

Сообщений: 25161
Откуда: Н.Новгород
Дата регистрации: 13.02.2008
> потеря целостности данных, когда поле suma не соответствует полю suma2.
а может структуру хранения данных переделать атк, чтобы SUMMA была записана в одной табличке? Тогда вопрос "потери целостности" не возникнет...
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
ssa

Сообщений: 12999
Откуда: Москва
Дата регистрации: 23.03.2005
ZenTigra
Как правильней суммировать поле suma, брать предыдущее значение (в table1) и добавлять (отнимать) новое
Ага, чтоб в случае наличия ошибки в этом поле так её и сохранять, не дай бох убежит...
Цитата:
, или каждый раз суммировать поле suma2 в table2. Записи в table2 очень часто добавляются (изменяются) (приблизительно 500-1000 записей в день).
Жуть, и как люди с бОльшими на порядки объемами работают...
Цитата:
Количество записей в table2 ~300000
Индексы, как утверждают некоторые, нужны ведь только для сортировки.
Цитата:

PS.В вдогонку, еще одна проблемка. Имя таблицы в программе не совпадает с алиасом. Как с этим работать в хранимой процедуре, обращаться к таблице нужно по ее имени или по алиасу.
И много вы, сударь, видели в БАЗЕ алиасов? Тем более, что в ПРОГРАММЕ одна и та же таблица может быть открыта под несколькими алиасами? А о том, что база и её ХП живут и БЕЗ приложения? О каком алаиасе может идти речь при отсутствии приложения?

------------------
Лень - это неосознанная мудрость.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
ssa

Сообщений: 12999
Откуда: Москва
Дата регистрации: 23.03.2005
ZenTigra
(Пожалуйста не советуйте делать проверку до момента сохранения, я хочу еще в хранимую процедуру засунуть проверку на ошибки ввода, или в триггерах это делать НЕЛЬЗЯ)
Хотеть не вредно, но о какой проверке какого ввода может идти речь при отсутствии приложения с этим самым вводом?

------------------
Лень - это неосознанная мудрость.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
pasha_usue

Сообщений: 3647
Откуда: Е-бург
Дата регистрации: 06.10.2006
При таких исходных данных лучше всего перевести базу данных на SQL. Куда угодно, хоть MS, хоть PG, хоть MY. И уже там осваивать триггеры. Или не осваивать. Не так уж много записей, что б денормализовывать таблицу.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
PaulWist

Сообщений: 14601
Дата регистрации: 01.04.2004
ZenTigra
...
Вопрос в логике, как это правильно делать.
Думаю, в форме где редактируется поле table2 при нажатии на кнопку «записать изменения» сделать код блокировки поля в table1, при успешной блокировке, произойдет сохранения записи, и автоматически выполниться триггер. Вот только хочется обойти подводные камни этой процедуры.


1. Подводные камни forum.foxclub.ru можно прочесть весь тред (там правда про II билдер).

2. Триггер необходимо писать не для одной записи, а для множества.

ZenTigra
...
Еще вопрос, не относящейся к triggers, но относящийся к этой задаче.
Как правильней суммировать поле suma, брать предыдущее значение (в table1) и добавлять (отнимать) новое, или каждый раз суммировать поле suma2 в table2. Записи в table2 очень часто добавляются (изменяются) (приблизительно 500-1000 записей в день).
Количество записей в table2 ~300000


Каждый раз суммировать.

Нативные таблицы фокса и триггеры не самые надёжные обьекты БД, лучше посмотреть на взрослые СУБД (бесплатные версии поддерживают БД до 10Г)

ZenTigra
...
PS.В вдогонку, еще одна проблемка. Имя таблицы в программе не совпадает с алиасом. Как с этим работать в хранимой процедуре, обращаться к таблице нужно по ее имени или по алиасу.

Посмотри по ссылке как реализуется в RI


------------------
Есть многое на свете, друг Горацио...
Что и не снилось нашим мудрецам.
(В.Шекспир Гамлет)
Ratings: 0 negative/1 positive
Re: Как работать с Trigger-ами?
PaulWist

Сообщений: 14601
Дата регистрации: 01.04.2004
ZenTigra
Еще одно проблема.
Поле suma - не должно быть нулевым, и если пользователь набрал a1 -120 (то есть поле suma будет -20) то как отменить сохранение запись? Как реализуется защита при СОХРАНЕНИИ на ввод неправильных данных. (Пожалуйста не советуйте делать проверку до момента сохранения, я хочу еще в хранимую процедуру засунуть проверку на ошибки ввода, или в триггерах это делать НЕЛЬЗЯ)

Надо использовать правило поля (хотя - это зло, лучше триггер), например

CREATE CURSOR test (f1 int CHECK f1 > 0)
INSERT INTO test VALUES (1)
UPDATE test SET f1 = -1


------------------
Есть многое на свете, друг Горацио...
Что и не снилось нашим мудрецам.
(В.Шекспир Гамлет)
Ratings: 0 negative/1 positive
Re: Как работать с Trigger-ами?
ZenTigra
Автор

Сообщений: 514
Дата регистрации: 03.12.2004
PaulWist
1. Подводные камни forum.foxclub.ru можно прочесть весь тред (там правда про II билдер).

Большое спасибо, почитаю.
Сразу все ломать не хочется, а нужно от чего то отталкиваться
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
AndyNigmatec

Сообщений: 1552
Откуда: Волгоград
Дата регистрации: 28.06.2015
PaulWist
бесплатные версии поддерживают БД до 10Г
... почему до 10? Вполне хорошо себя чуЙствуют и бОльшие )))
Правда более 80-100 уже как-то некомфортно ими ворочать без нормального железа.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
pasha_usue

Сообщений: 3647
Откуда: Е-бург
Дата регистрации: 06.10.2006
AndyNigmatec
PaulWist
бесплатные версии поддерживают БД до 10Г
... почему до 10? Вполне хорошо себя чуЙствуют и бОльшие )))
Правда более 80-100 уже как-то некомфортно ими ворочать без нормального железа.
Это ограничение MSSQL Express. Для Oracle XE вроде 12гб.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
leonid

Сообщений: 3202
Откуда: Рига
Дата регистрации: 03.02.2006
pasha_usue
Это ограничение MSSQL Express. Для Oracle XE вроде 12гб.

А для Postgres ...

Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
pasha_usue

Сообщений: 3647
Откуда: Е-бург
Дата регистрации: 06.10.2006
leonid
pasha_usue
Это ограничение MSSQL Express. Для Oracle XE вроде 12гб.

А для Postgres ...
Ну... За что я и люблю постгрес. Функционал почти оракловский, и ограничений по-сути нет.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
leonid

Сообщений: 3202
Откуда: Рига
Дата регистрации: 03.02.2006
pasha_usue
Функционал почти оракловский
Ну, все-таки не дотягивает. Хинтов нет, статистики нет.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
ZenTigra
Опишу маленьким примером ту задачу, которую хочу решить
Вопросы поддержания согласованности денормализованных данных через триггера не лучшим образом решаются... Тем более что из приведенной информации вообще не очевидно что данные необходимо денормализовывать. Может быть для начала просто провести оптимизацию - ускорить выборки для которых необходимы эти самые "суммы по коду"?
Тем более что защитить от "неправильного" изменения "автоматически вычисляемое триггером поле" ты всё равно не сможешь...
ZenTigra
Сейчас вся логика обработки находиться в программе, но иногда случаются потеря целостности данных, когда поле suma не соответствует полю suma2. Думаю что это происходит из-за того, что поле suma2 изменяться из многих мест программы, и есть вероятность ошибки в коде. Вот я и хочу собрать этот код в одном месте (в хранимой процедуре БД), чтоб избавиться от этих ошибок.
Хранимая процедура и триггер это совершенно не связанные друг с другом вещи. Да, можно написать ХП которая будет синхронно менять данные в 2 (и более) таблицах, и работать с этими таблицами исключительно при помощи этих самых ХП - НО в фоксе нет способов запретить программе обращаться к таблице напрямую - точнее эти способы будут крайне ненадёжными, и по сути кривыми. Да и обойти их всегда можно будет без особого труда. А вот найти в программе все места где так или иначе меняются "напрямую" данные в таблице и заменить там эту прямую работу на работу через ХП - не самая простая и лёгкая работа.
ZenTigra
Думаю, в форме где редактируется поле table2 при нажатии на кнопку «записать изменения» сделать код блокировки поля в table1, при успешной блокировке, произойдет сохранения записи, и автоматически выполниться триггер. Вот только хочется обойти подводные камни этой процедуры.
Какая-то ересь. Какое отношение блокировка из кода программы имеет к триггерам? Триггер выполняется автоматически, при любой попутке изменения данных в таблице - неважно заблокирована там запись вручную или нет, открыта "снаружи" транзакция или нет, производится модификация данных командой (UPDATE/REPLACE/INSERT/DELETE) или банально в BROWSE окне поменяют значение в поле.
Наверное для начала следует вдумчиво почитать что такое триггера, как они работают, а потом раз 5-10 изучить код триггеров и вспомогательных ХП, которые генерирует фоксовый "мастер ссылочной целостности" aka RI Builder. Лучше изучать его как раз на самых примитивных и простых примерах - потому что для реальных таблиц он (сгенерированный код) может оказаться ну очень уж массивным по объёму.
ZenTigra
Еще вопрос, не относящейся к triggers, но относящийся к этой задаче.
Как правильней суммировать поле suma, брать предыдущее значение (в table1) и добавлять (отнимать) новое, или каждый раз суммировать поле suma2 в table2. Записи в table2 очень часто добавляются (изменяются) (приблизительно 500-1000 записей в день).
Количество записей в table2 ~300000
Этот вопрос имеет самое прямое отношение к тому из триггера будет это исполняться или нет, и будет ли находится таблица в одном из режимов буферизации или нет. Поскольку для "просто кода" банально негде будет взять "предыдущее значение из поля" - без чего невозможно считать разницу и её уже "добавлять/отнимать" к полю хранящему сумму. А с другой стороны, изнутри триггера не так то и просто обращаться к той же самой таблице для которой произошло срабатывание триггера... Поэтому в отрыве от вопроса ГДЕ этот код будет написан, и в КАКОМ СОСТОЯНИИ будут находится обе таблицы (буферизация, блокировки, в т.ч. неявные которые будут иметь место внутри тела триггера) - невозможно дать ответ.
ZenTigra
PS.В вдогонку, еще одна проблемка. Имя таблицы в программе не совпадает с алиасом. Как с этим работать в хранимой процедуре, обращаться к таблице нужно по ее имени или по алиасу.
В абстрактной ХП можно просто обращаться к текущей таблице (текущей рабочей области, алиас которой можно получить при помощи ALIAS или номер рабочей области при помощи SELECT() ), а вторую таблицу специально открывать под новым алиасом в новой рабочей области - можно даже закрывать её сразу после внечения необходимых изменений. В триггере ещё добавляется немного возни и ограничений - но в целом после изучения кода триггеров генерируемых RI builder-ом это должно быть примерно понятно.
ZenTigra
Поле suma - не должно быть нулевым, и если пользователь набрал a1 -120 (то есть поле suma будет -20) то как отменить сохранение запись?
Если триггер возвращает .F. то фокс отменяет модификацию вызвавшую срабатывание триггера, и генерирует ошибку, которую можно поймать и обработать штатным образом.
Другое дело что у тебя явно будут сложности в реализации логики "согласования" поля суммы с суммой полей второй таблицы.
Потому ещё раз - подумай о том настолько ли необходима в данном случае денормализация, и не проще ли просто всегда когда необходимо считать эту самую сумму по полям одной таблицы.


------------------
WBR, Igor




Исправлено 1 раз(а). Последнее : Igor Korolyov, 08.04.20 23:06
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
Igor Korolyov

Сообщений: 34580
Дата регистрации: 28.05.2002
ssa
Хотеть не вредно, но о какой проверке какого ввода может идти речь при отсутствии приложения с этим самым вводом?
О той, которая реализуется триггерами и правилами проверки (field/record validation rules), и которые работают совершенно независимо от приложения, и даже безо всяких приложений, тупо при открытии таблицы в IDE фокса и попытке чего-то там поменять в BROWSE окне.
Вопрос лишь в том какие именно проверки имеет смысл реализовывать на уровне БД (тем более фоксовой, где любую "защиту" можно обойти буквально за минуту). И в том, не проще ли банально нормализовать структуру хранения, чтобы не возникало надобности в некоторых "проверках" - типа того что сумма в таблице "остатков" соответствует данным в таблицах "движения".

PaulWist
2. Триггер необходимо писать не для одной записи, а для множества.
В фоксе такой возможности нет. Фоксовые триггера всегда работают с изменением ровно одной записи (если была исполнена команда меняющая 100500 записей, то просто триггер сработает 100500 раз).
PaulWist
Надо использовать правило поля
В данном случае правило поля не годится, т.к. речь идёт о правиле "сумма по полю amount для всех записей с определённым code должна быть больше нуля". Правило поля применяют для проверок корректности данных только в одном поле одной записи, правило записи - для проверки коррекности затрагивающей разные поля одной записи, а когда речь заходит о группе записей, или записях из другой таблицы (классический пример referential integrity) то это уже работа для триггера.


------------------
WBR, Igor
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
Владимир Максимов

Сообщений: 14095
Откуда: Москва
Дата регистрации: 02.09.2000
"Перевожу" постановку задачи

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

По особенностям использования триггера в FoxPro можно почитать здесь

Триггер

Применительно к данной задаче

1. Внутри тела триггера автоматически окажешься в рабочей области изменяемой "строки". Как следствие, прямо в ней можно использовать OldVal(), чтобы получить значение до изменения. И далее получаешь разницу с текущим значением.

2. Следовательно, остается только найти запись "шапки" и прибавить к итоговой сумме найденную разницу


Вопрос в том, как именно организовать это "прибавление"

Для текущей строки новую рабочую область открывать не надо. Мы в ней и так находимся. А вот для таблицы "шапки" внутри тела триггера надо именно что открыть новую рабочую область. Если "шапка" уже открыта вне триггера все равно нельзя использовать эту рабочую область. Надо открывать новую

Поскольку у нас есть еще дополнительное условие, что итоговая сумма в шапке не должна получиться отрицательной, то нельзя использовать Update-SQL. Тут надо будет сделать банальный поиск через seek+if+replace

Т.е. открываем рабочую область шапки, через seek находим запись, проверяем, что после прибавления разницы сумма останется положительной. Если "Да", то вносим изменения и триггер возвращает .T., если "Нет", то триггер возвращает .F. и все изменения откатываются

Проверка внутри триггера - это не ДО сохранения. Это именно что "в процессе" сохранения

--------------------------

Дополнительные вопросы

1. Рассчитывать сумму в шапке заново по всем строкам или только на разницу?

На момент выполнения триггера данные по текущей строке еще не сохранены. Т.е. рассчитать-то сумму можно, но по текущей строке все-равно придется разницу добавлять.

При этом с точки зрения равенства суммы в шапке и строках вероятность получить расхождения будет точно такой же. Я не вижу здесь никаких преимуществ. Только времени будет больше тратиться


2. Блокировки

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

А так, нет никакого смысла в блокировке. Она не повлияет на корректность работы триггера.


3. В тело триггера добавить проверки на ошибки ввода

Можно, конечно. Но это не "естественная" задача для триггера. Да и триггер выполняется слишком "поздно" для контроля ввода

Цель триггера - это поддержание целостности данных. Синхронное изменение данных в разных записях и в разных таблицах. Вот обновление суммы в шапке - задача для триггера. А контроль ввода - это несколько другое и выполняется в других местах. В частности, в RULE уровня поля или записи

Если все-таки контроль ввода будет перенесен в триггер, то следует иметь в виду, что штатно не предусмотрено возвращение текста ошибки из триггера. Он возвращает только логическое значение. .T. - успех, .F. - ошибка и отмена внесенных изменений

Для возврата текста ошибки используют "костыль" в виде глобальной переменной (в штатной RI для этого используется массив gaErrors). Т.е. в случае ошибки в теле триггера сначала записывают текст ошибки в глобальную переменную, потом возвращают .F. А уже вне тела триггера анализируют код ошибки при сохранении и если это ошибка триггера, то считывают текст из этой глобальной переменной



Исправлено 1 раз(а). Последнее : Владимир Максимов, 09.04.20 01:53
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
Опасное это слово - "триггер", раздразнили гусей.
Даже описания работы с триггерами не менее страшны, чем сами триггеры.
И все это вместо небольшого CUSTOM-класса с парочкой процедур.
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
ZenTigra
Автор

Сообщений: 514
Дата регистрации: 03.12.2004
lulgu
Опасное это слово - "триггер", раздразнили гусей.
Даже описания работы с триггерами не менее страшны, чем сами триггеры.
И все это вместо небольшого CUSTOM-класса с парочкой процедур.

Вот мне и нужно выяснить, стоит ли в моем случае связываться с триггерами.
Я не вполне понимаю, для каких задач они применяются, вот спросил помощи у форумчан, и их ответом вполне доволен.
Теперь буду пробовать, а нужно ли оно мне, или хватит класса с парочкой процедур.

PS.Всем спасибо за советы.



Исправлено 1 раз(а). Последнее : ZenTigra, 09.04.20 09:19
Ratings: 0 negative/0 positive
Re: Как работать с Trigger-ами?
lulgu

Сообщений: 1838
Дата регистрации: 30.11.2016
Фоксовские триггеры для практической работы непригодны, все это бла-бла-бла уже два десятка лет..
Такие задачи в фоксе практики обычно решают кто как может, так проще.
А теоретики козыряют наборами SQL-языков, как своими.

ЗЫ. В принципе, можно создать универсальный класс _Trigger, куда с умом вынести коды из RI
Только смысла в этом немного, это возня на любителя.



Исправлено 2 раз(а). Последнее : lulgu, 09.04.20 09:46
Ratings: 0 negative/0 positive


Извините, только зарегистрированные пользователи могут оставлять сообщения в этом форуме.

On-line: 60 OlegA  (Гостей: 59)

© 2000-2024 Fox Club 
Яндекс.Метрика