powered by simpleCommunicator - 2.0.56     © 2025 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / процедурка на ins, upd и del
8 сообщений из 8, страница 1 из 1
процедурка на ins, upd и del
    #35945591
Imperous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Всем привет!
Вот стало интересно написать процедрку-триггер на табличку.
Смысл таков - в одной таблице значения, а в другой сумма.
Например:
табличка 1 - tab1
id_tab vsego110215320

табличка 2 - tab2
id_tab (autoinc) id_tab1 (ссылка на tab1.id_tab) kol_vo (числа которые добавляются меняются удаляются)11521532104255310635735

Теперь, например мы делаем следующее:
Код: plaintext
insert into tab2 (id_tab1, kol_vo) values( 1 ,  40 )
Код: plaintext
update tab2 set kol_vo =  15  where id_tab =  4 
Код: plaintext
delete from tab2 where id_tab = 6 

Собственно триггер должен выполнить примерно это:
procedure my_proc()

*1. взять строку на которой стоим, и из нее вытащить id_tab1 чтобы дальше сделать выборку:
Код: plaintext
  my = tab2.id_tab1
*2. Ну и собственно, вот:
Код: plaintext
1.
2.
   update tab1 ;
     set vsego = (select sum(kol_vo) from tab2 where id_tab1 = my) 
     where id_tab = my

endproc

Результат должен быть такой:
id_tab vsego150 (добавили строчку со значением 40)225 (увеличили на 10)315 (удалили 6-ю строку со значением 5)


Вставляю эту процедуру как триггер на upd ins del
C удалением понимаю - лажа должна выходить, так как строки уже нет.

Только если честно, то получается ерунда...
Подскажите плиз, почему?
...
Рейтинг: 0 / 0
процедурка на ins, upd и del
    #35950492
Imperous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
кстати, ошибся, с удалением лажи быть не должно, ведь сама строка не удаляется, а всего лишь помечается на удаление, т.е. курсор остается именно на ней.
...
Рейтинг: 0 / 0
процедурка на ins, upd и del
    #35950783
LUCIAN
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Imperous
Только если честно, то получается ерунда...
Подскажите плиз, почему?
Получается ерунда наверно потому что буферизация не учтена.
...
Рейтинг: 0 / 0
процедурка на ins, upd и del
    #35951377
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не надо сканировать ту таблицу, модификации в которой и выполняются. Лучше сделайте более простой триггер

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
LOCAL lnKol_vo, lnKol_vo_Prev, lnID

* Текущее значение в поле количество
lnKol_vo = kol_vo

* Значение в поле количество, которое было до модификации
* Для новой записи это будет NULL, даже если поле не допускает такого значения
lnKol_vo_Prev = OldVal("kol_vo")

* Код записи родительской таблицы
lnID = id_tab1

DO CASE  
CASE Deleted()  
	* вызов из триггера на удаление  
	UPDATE tab1 ;
		set vsego = vsego - m.lnKol_vo ;
		where id_tab = m.lnID
CASE NVL(OldVal("Deleted()"),.T.)  
	* вызов из триггера на вставку  
	UPDATE tab1 ;
		set vsego = vsego + m.lnKol_vo ;
		where id_tab = m.lnID
OTHERWISE  
	* вызов из триггера на модификацию  
	UPDATE tab1 ;
		set vsego = vsego - m.lnKol_vo_Prev + m.lnKol_vo ;
		where id_tab = m.lnID
ENDCASE  

Дело разумеется, вовсе не в буферизации. Точнее, не в той буферизации, которая накладывается через CursorSetProp(). Дело в том, что "по определению" триггер - это "вратарь". Последний контролер, который принимает решения записать-таки сделанные изменения в таблицу или нет.

Но, раз данные еще не записаны, то что же вы планируете считывать через запросы? Ведь запрос - это обращение напрямую к таблице-источнику. А в ней внесенных изменений пока еще нет. Они есть только в текущей рабочей области.

Вот вам подзапрос по tab2 "фигню" и возвращает. Точнее, он возвращает данные по состоянию ДО модификации.

А прямое чтение поля таблицы в текущей рабочей области как раз и вернет значение ПОСЛЕ модификации.

Триггера FoxPro обрабатывают по одной записи за раз. Групповая обработка записей невозможна. Поэтому внутри тела триггера всегда находимся на одной записи таблицы, вызвавшей срабатывание триггера.
...
Рейтинг: 0 / 0
процедурка на ins, upd и del
    #35952237
LUCIAN
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ВладимирМ,
Когда заменил свой аналогичный триггер:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
local idk,sumak,SUMAS
local ARRAY ITDOK( 1 )
SELE DOKS
IDK=DOKS.ID_DOK
IF IDK= 0 
	RETURN
ENDIF	
SUMAK=DOKS.SUMA
SUMAS=OLDVAL("SUMA")
sumas=IIF(ISNULL(SUMAS), 0 ,SUMAS)
SELECT SUM(SUMA) FROM DOKS WHERE ID_DOK=IDK GROUP BY ID_DOK INTO ARRAY ITDOK
IF _TALLY= 0 
	RETURN
ENDIF
UPDATE SKLAD!DOK SET SUMA=ITDOK( 1 )+SUMAK-SUMAS WHERE ID_DOK=IDK

на триггер по схеме ВладимирМ то получил такой
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
local idk,sumak,SUMAS

* Текущее значение в поле количество
SUMAK=DOKS.SUMA
IDK=DOKS.ID_DOK

* Значение в поле количество, которое было до модификации
* Для новой записи это будет NULL, даже если поле не допускает такого значения
SUMAS=OLDVAL("SUMA")

* Код записи родительской таблицы
IDK=DOKS.ID_DOK

DO CASE  
CASE Deleted()  
	* вызов из триггера на удаление  
	UPDATE DOK;
		set SUMA = SUMA - m.SUMAK ;
		where id_DOK = m.IDK
CASE NVL(OldVal("Deleted()"),.T.)  
	* вызов из триггера на вставку  
	UPDATE DOK ;
		set SUMA = SUMA + m.SUMAK ;
		where id_DOK = m.IDK
OTHERWISE  
	* вызов из триггера на модификацию  
	UPDATE DOK ;
		set SUMA = SUMA - m.SUMAS + m.SUMAK ;
		where id_DOK = m.IDK
ENDCASE  

то он успешно заработал,хотя не могу обьяснить смысл:
Код: plaintext
1.
2.
CASE NVL(OldVal("Deleted()"),.T.)  
	* вызов из триггера на вставку  
 
...
Рейтинг: 0 / 0
процедурка на ins, upd и del
    #35952273
Imperous
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ВладимирМ,
понятно, пасиб :)
...
Рейтинг: 0 / 0
процедурка на ins, upd и del
    #35952920
Фотография ВладимирМ
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
LUCIAN

Вообще-то, конечно, в данном случае можно обойтись и без этого анализа. Написав примерно так

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
LOCAL lnKol_vo, lnKol_vo_Prev, lnID

* Текущее значение в поле количество
lnKol_vo = kol_vo

* Значение в поле количество, которое было до модификации
* Для новой записи это будет NULL, даже если поле не допускает такого значения
lnKol_vo_Prev = NVL(OldVal("kol_vo"), 0 )

* Код записи родительской таблицы
lnID = id_tab1

UPDATE tab1 ;
	set vsego = vsego - m.lnKol_vo_Prev + m.lnKol_vo ;
	where id_tab = m.lnID

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

Анализ производится через оператор CASE по той причине, что данный оператор прерывает свое выполнение в тот момент, когда очередной CASE примет значение .T. Это значит, что если перешли на очередной CASE, то все предыдущие вернули .F.

Первой выполняется проверка на удаление. Если функция Deleted() вернула .T., то данная функция была вызвана из триггера на удаление. Вызова из триггера на модификацию не может быть по той причине, что при модификации записей, помеченных как удаленные триггер на модификацию не срабатывает. Игнорируется.

Следующая проверка выглядит так

Код: plaintext
CASE NVL(OldVal("Deleted()"),.T.)

Раз мы попали на эту проверку, то данную функцию вызвал либо триггер на вставку, либо триггер на модификацию.

Триггер на вставку может сработать по двум причинам:

1. При физическом создании новой записи
2. По команде RECALL при снятии метки на удаление

Если дали команду Recall, то, очевидно, что OldVal("Deleted()") вернет .T. Ведь до модификации записи эта запись была помечена как удаленная. Однако напомню, что в данный момент запись НЕ помечена как удаленная. Это уже проверили на предыдущем этапе.

Если же это физическое создание новой записи, то OldVal() по любому выражению вернет NULL.

Вот и получается, что это условие читается примерно так:

До модификации был установлен признак удаленной записи, который сейчас сняли ИЛИ это новая запись

Это условие может вернуть .F. только и исключительно тогда, когда ДО модификации Deleted()=.F. Т.е. запись существовала и это не есть удаление, поскольку этот факт был проверен еще на первом CASE. Значит, Otherwise - это только и исключительно модификация.

=============================

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

Код: plaintext
1.
SELE DOKS
IDK=DOKS.ID_DOK

потенциально опасна. Может привести к не предсказуемым последствиям.

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

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

Код: plaintext
SELECT DOKS

разумеется, сделает переключение в нужную рабочую область, однако не факт, что рабочая область с именем "DOKS" - это та область, где и были выполнены модификации.

Поэтому, если так уж хочется явно указывать алиас таблицы, то это придется делать через макроподстановки. Примерно так

Код: plaintext
1.
2.
3.
LOCAL lcAlias
lcAlias = Alias()
IDK=Evaluate(m.lcAlias+".ID_DOK")

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

Команда Update-SQL автоматически открывает ту таблицу, в которой выполняются модификации. Поэтому, если талица не была открыта до выполнения команды Update-SQL необходимо ее закрыть после выполнения.
...
Рейтинг: 0 / 0
процедурка на ins, upd и del
    #35953982
LUCIAN
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
ВладимирМ,
Спасибо за подробное объяснение. Мои попытки организовать с помощью триггеров автоматическое
формирование текущих остатков материалов на складах были не успешны.Попытаюсь в ближайшее
время решить эту проблему с помощью вышеуказанной схемы организации триггера.
...
Рейтинг: 0 / 0
8 сообщений из 8, страница 1 из 1
Форумы / FoxPro, Visual FoxPro [игнор отключен] [закрыт для гостей] / процедурка на ins, upd и del
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]