То ли глюк, то ли... непонятно...
CREATE TRIGGER After_raschet_upd FOR raschet AFTER UPDATE
AS
DECLARE VARIABLE ps NUMERIC(10,2); -- сумма на начало и на конец месяца
DECLARE VARIABLE pi NUMERIC(10,2); -- итог на месяц
BEGIN
FOR SELECT * FROM raschet WHERE (N_Tel = OLD.N_Tel) and (G_M like '%/01') DO
BEGIN
if (dolg_n>0) then ps=DOLG_N; if (DOLG_N<=0) then ps=-AVANS_N; -- закинули сумму на начало
END
FOR SELECT * FROM raschet WHERE (N_Tel = OLD.N_Tel) ORDER BY G_M DO
BEGIN
pi = ABON_PL + MP + DT_MP + ABON_PL_I + ABON_PL_O + PERERASCHET + DU; -- вычислили итог
ps = :ps + :pi - OPLATA; -- вычислили остаток на конец месяца
/* код не готовый до конца... */
END
END
FireBird 2.0 + IBX 2007.06.13
Вот в чем проблема? Регается на "DO".
/************************************
The next statement causes the following error:
Invalid token.
Dynamic SQL Error.
SQL error code = -104.
Token unknown - line 7, column 76.
DO.
*************************************
Устал уже с ним воевать...
Цитирую (стр. 170)
FOR SELECT is a loop statement that retrieves the row specified in the select_expr and performs the statement or block following DO for each row retrieved.
The select_expr is a normal SELECT, except the INTO clause is required and must be the last clause.
Вы забыли указать INTO, что является обзательным в этой конструкции.
И еще... Я, конечно, не знаю сути всей решаемой задачи, но циклы какие-то странные... Что выбираем? Одну запись или несколько? Если одну тогда зачем вообще FOR SELECT, если несколько, тогда какой смысл в присваивании значений переменным ps в первом цикле.
Подскажите, кто знает, как сделать чтобы триггер вызывался только один раз за транзакцию. или какой-нить другой вариант решения.
CREATE TRIGGER After_raschet_upd FOR raschet AFTER UPDATE
AS
DECLARE VARIABLE pg_m CHAR(7);
DECLARE VARIABLE pd_n NUMERIC(10,2); -- долг на начало
DECLARE VARIABLE pa_n NUMERIC(10,2); -- аванс на начало
DECLARE VARIABLE pAbon_pl NUMERIC(10,2);
DECLARE VARIABLE pMP NUMERIC(10,2);
DECLARE VARIABLE pDT_MP NUMERIC(10,2);
DECLARE VARIABLE pAbon_pl_i NUMERIC(10,2);
DECLARE VARIABLE pAbon_pl_o NUMERIC(10,2);
DECLARE VARIABLE pPereraschet NUMERIC(10,2);
DECLARE VARIABLE pDU NUMERIC(10,2);
DECLARE VARIABLE pItog NUMERIC(10,2);
DECLARE VARIABLE pOplata NUMERIC(10,2);
DECLARE VARIABLE pd_k NUMERIC(10,2);
DECLARE VARIABLE pa_k NUMERIC(10,2);
DECLARE VARIABLE ps NUMERIC(10,2); -- сумма на начало и на конец месяца
BEGIN
SELECT dolg_n, avans_n FROM raschet WHERE (N_Tel = OLD.N_Tel) and (G_M like '%/01') -- выбрали начальные данные
INTO :pd_n, :pa_n;
IF (:pd_n>0) THEN ps=:pd_n;
ELSE ps=-:pa_n;
/* цикл перерасчета */
FOR SELECT G_M,DOLG_N,AVANS_N,ABON_PL,MP,DT_MP,ABON_PL_I,ABON_PL_O,PERERASCHET,DU,ITOG,OPLATA,DOLG_K,AVANS_K
FROM raschet WHERE (N_Tel = OLD.N_Tel) ORDER BY G_M
INTO :pG_M,:pd_n,:pa_n,:pAbon_pl,:pMP,:pDT_MP,:pAbon_pl_i,:pAbon_pl_o,:pPereraschet,:pDU,:pItog,:pOplata,:pd_k,:pa_k
DO
BEGIN
IF (:ps > 0) THEN
BEGIN
pd_n = :ps;
pa_n = 0.00;
END
ELSE
BEGIN
pd_n = 0.00;
pa_n = -:ps;
END -- вычислили значения на начало месяца
pDT_MP = :pMP * 0.18; -- вычислили доп.тариф на м/г и м/н связь
pItog = :pAbon_pl + :pMP + :pDT_MP + :pAbon_pl_i + :pAbon_pl_o + :pPereraschet + :pDU; -- вычислили итог
ps = :ps + :pItog - :pOplata; -- сумма на конец месяца
IF (:ps > 0) THEN
BEGIN
pd_k = :ps;
pa_k = 0.00;
END
ELSE
BEGIN
pd_k = 0.00;
pa_k = -:ps;
END -- вычислили значения на конец месяца
UPDATE raschet
SET DOLG_N=:pd_n, AVANS_N=:pa_n, DT_MP = :pDT_MP, ITOG = :pItog, DOLG_K = :pd_k, AVANS_K = :pa_k
WHERE (N_Tel = OLD.N_Tel) and (G_M = :pG_M);
END
END
Подскажите, кто знает, как сделать чтобы триггер вызывался только один раз за транзакцию. или какой-нить другой вариант решения.
Отказаться от триггера, а написать хранимую процедуру, в которой реализовать всю необходимую логику
мммм. Я бы посоветовал использовать замещающий триггер - не
а
если конечно в птице это реализовано (не помню)
Все таки разница между триггером и ХП достаточно значительна.
а
если конечно в птице это реализовано (не помню)
Все таки разница между триггером и ХП достаточно значительна.
В FB насколько я знаю можно создавать триггеры на VIEW (не помню правда начиная с какой версии сервера), может через них можно извернуться, но я бы все таки лучше сделал через хранимку. Т.е. отказаться от прямого update таблицы, а вместо этого вызывать ХП, в которой реализована вся бизнес-логика.
Во второй уже должно быть реализовано (по крайней мере обещали :))
Все это верно - но дело в том что апдатиться таблица не обязательно на прямую - как правило это всегда делается через ХП (я по крайней мере всегда делаю так) - а использование триггера как правило необходимо для разделения бизнес-логики и "низкоуровневой" логики. В качестве примера, можно привести установку флага активности для последней вставленной записи - ИМХО, это уровень не бизнес-логики (которая реализуется в ХП) и более правильно для этого использовать именно триггер.
Все это верно - но дело в том что апдатиться таблица не обязательно на прямую - как правило это всегда делается через ХП (я по крайней мере всегда делаю так) - а использование триггера как правило необходимо для разделения бизнес-логики и "низкоуровневой" логики. В качестве примера, можно привести установку флага активности для последней вставленной записи - ИМХО, это уровень не бизнес-логики (которая реализуется в ХП) и более правильно для этого использовать именно триггер.
Я в принципе согласен с вами, хотя я стараюсь использовать триггеры как можно реже, т.к., на мой взгляд, они снижают понимание работы программы. Но если вернуться к проблеме автора, то у него как раз в данном случае в триггере происходит выполнение бизнес-логики, более того изменяется таблица по которой сработал триггер, что совсем уж нехорошо. Так что, повторяю, я бы вынес все это в отдельную ХП.
Еще один вопрос: можно ли обращаться к полям таблицы не по имени, а по номеру. Если можно, то какаая конструкция?
Еще один вопрос: можно ли обращаться к полям таблицы не по имени, а по номеру. Если можно, то какаая конструкция?
Только недавно такая тема была:
http://forum.codenet.ru/showthread.php?t=44192
В общем случае нельзя.
CREATE PROCEDURE GO_ARCHIVE_ON_NEW_YEAR (pYear CHAR(4))
/* процедура перехода на новый год */
AS
DECLARE VARIABLE pN_TEL NUMERIC(10,0);
DECLARE VARIABLE pFIO_NAIM VARCHAR(512) ;
DECLARE VARIABLE pADRESS_URID VARCHAR(512);
DECLARE VARIABLE pADRESS_FAKT VARCHAR(512);
DECLARE VARIABLE pINN VARCHAR(12);
DECLARE VARIABLE pSTATUS VARCHAR(8);
DECLARE VARIABLE pRAION VARCHAR(20);
DECLARE VARIABLE pTP VARCHAR(512);
DECLARE VARIABLE pABON_PL NUMERIC(10,2);
DECLARE VARIABLE pKAT_PORT VARCHAR(10);
DECLARE VARIABLE pRADIO_BAZA VARCHAR(10);
DECLARE VARIABLE pTP_I VARCHAR(512);
DECLARE VARIABLE pABON_PL_I NUMERIC(10,2);
DECLARE VARIABLE pKAT_PORT_I VARCHAR(15);
DECLARE VARIABLE pABON_PL_O NUMERIC(10,2);
DECLARE VARIABLE pZAYAVLEN VARCHAR(10);
DECLARE VARIABLE pDATA_ZAYAVLEN VARCHAR(10);
DECLARE VARIABLE pNARYAD VARCHAR(10);
DECLARE VARIABLE pDATA_NARYAD VARCHAR(10);
DECLARE VARIABLE pDOGOVOR VARCHAR(10);
DECLARE VARIABLE pDATA_DOGOVOR VARCHAR(10);
DECLARE VARIABLE pZAYAVLEN_I VARCHAR(10);
DECLARE VARIABLE pDATA_ZAYAVLEN_I VARCHAR(10);
DECLARE VARIABLE pNARYAD_I VARCHAR(10);
DECLARE VARIABLE pDATA_NARYAD_I VARCHAR(10);
DECLARE VARIABLE pDOGOVOR_I VARCHAR(10);
DECLARE VARIABLE pDATA_DOGOVOR_I VARCHAR(10);
DECLARE VARIABLE pDOGOVOR_O VARCHAR(10);
DECLARE VARIABLE pDATA_DOGOVOR_O VARCHAR(10);
DECLARE VARIABLE pTEL_ON CHAR(1);
DECLARE VARIABLE pINET_ON CHAR(1);
DECLARE VARIABLE pOBSL_ON CHAR(1);
DECLARE VARIABLE pPRIMECH VARCHAR(1024); -- переменные для главной базы
DECLARE VARIABLE pTEL NUMERIC(10,0); -- просто так... :)
DECLARE VARIABLE pflag char(1); -- флаг для генератора, вдруг такой номер уже есть в архивной таблице
DECLARE VARIABLE pN_PP INTEGER; -- переменная для генератора
DECLARE VARIABLE ps NUMERIC(10,2); -- переменная для аванса или долга на новый год
DECLARE VARIABLE pg_m char(7);
DECLARE VARIABLE pmesyac varchar(8);
DECLARE VARIABLE pgod char(4);
DECLARE VARIABLE pdolg_n numeric(10,2);
DECLARE VARIABLE pavans_n numeric(10,2);
DECLARE VARIABLE pabon_pl_r numeric(10,2);
DECLARE VARIABLE pMP numeric(10,2);
DECLARE VARIABLE pDT_MP numeric(10,2);
DECLARE VARIABLE pabon_pl_i_r numeric(10,2);
DECLARE VARIABLE pabon_pl_o_r numeric(10,2);
DECLARE VARIABLE ppereraschet numeric(10,2);
DECLARE VARIABLE pDU numeric(10,2);
DECLARE VARIABLE pitog numeric(10,2);
DECLARE VARIABLE poplata numeric(10,2);
DECLARE VARIABLE pdolg_k numeric(10,2);
DECLARE VARIABLE pavans_k numeric(10,2); -- переменные для таблицы рассчета
DECLARE VARIABLE pDATA VARCHAR(10);
DECLARE VARIABLE pVREMYA VARCHAR(5);
DECLARE VARIABLE pNAIM VARCHAR(254);
DECLARE VARIABLE pPRODOLJ integer;
DECLARE VARIABLE pSUMMA NUMERIC(10,2);
DECLARE VARIABLE pNAPRAV VARCHAR(254); -- переменные для м/г и м/н переговоров
BEGIN
FOR SELECT * FROM base_main INTO :pN_tel, -- выбираем каждый № телефона
:pFIO_naim,
:pAdress_urid,
:pAdress_fakt,
:pINN,
:pStatus,
:pRaion,
:pTP,
:pAbon_pl,
:pKat_port,
:pRadio_baza,
:pTP_i,
:pAbon_pl_i,
:pKat_port_i,
:pAbon_pl_o,
:pZayavlen,
:pData_zayavlen,
:pNaryad,
:pData_naryad,
:pDogovor,
:pData_dogovor,
:pZayavlen_i,
:pData_zayavlen_i,
:pNaryad_i,
:pData_naryad_i,
:pDogovor_i,
:pData_dogovor_i,
:pDogovor_o,
:pData_dogovor_o,
:pTel_on,
:pInet_on,
:pObsl_on,
:pPrimech -- заполнили переменные для главной таблицы
DO
BEGIN
pFLAG = '0'; -- 0 значит такой номер уже есть в арх.таблице
pN_PP = GEN_ID(archive_base_main_gen,1); -- увеличили генератор на 1
WHILE (:pFLAG = '0') DO -- ищем свободный номер в арх.таблице
BEGIN
IF ((SELECT COUNT(N_PP) FROM archive_base_main WHERE N_PP=:pN_PP)>0)
THEN pN_PP = GEN_ID(archive_base_main_gen,1);
ELSE
pFLAG = '1';
END
INSERT INTO archive_base_main
VALUES (:pN_PP,:pN_tel,:pFIO_naim,:pAdress_urid,:pAdress_fakt,:pINN,:pStatus,
:pRaion,:pTP,:pAbon_pl,:pKat_port,:pRadio_baza,:pTP_i,:pAbon_pl_i,
:pKat_port_i,:pAbon_pl_o,:pZayavlen,:pData_zayavlen,:pNaryad,
:pData_naryad,:pDogovor,:pData_dogovor,:pZayavlen_i,:pData_zayavlen_i,
:pNaryad_i,:pData_naryad_i,:pDogovor_i,:pData_dogovor_i,:pDogovor_o,
:pData_dogovor_o,:pTel_on,:pInet_on,:pObsl_on,:pPrimech); -- внесли данные из гл.табл в арх.гл.табл.
SELECT * FROM raschet WHERE (N_TEL = :pN_TEL) and (G_M like '%/12') INTO :ps; -- запомнили долг или аванс
FOR SELECT * FROM raschet WHERE (N_TEL = :pN_TEL) ORDER BY G_M INTO :pTEL,
:pg_m,
:pmesyac,
:pgod,
:pdolg_n,
:pavans_n,
:pabon_pl_r,
:pMP,
:pDT_MP,
:pabon_pl_i_r,
:pabon_pl_o_r,
:ppereraschet,
:pDU,
:pitog,
:poplata,
:pdolg_k,
:pavans_k
DO -- перекидываем данные в архивную табл. расчет
BEGIN
INSERT INTO archive_raschet
VALUES (:pN_PP,:pN_TEL,:pg_m,:pmesyac,:pgod,:pdolg_n,:pavans_n,:pabon_pl_r,
:pMP,:pDT_MP,:pabon_pl_i_r,:pabon_pl_o_r,:ppereraschet,:pDU,:pitog,
:poplata,:pdolg_k,:pavans_k);
END
FOR SELECT * FROM mp WHERE N_TEl = :pN_TEL INTO :pTEL,
:pG_M,
:pDATA,
:pVREMYA,
:pNAIM,
:pPRODOLJ,
:pSUMMA,
:pNAPRAV
DO -- перекидываем данные из МП в арх.МП
BEGIN
INSERT INTO archive_mp
VALUES (:pN_PP,:pN_TEL,:pG_M,:pDATA,:pVREMYA,:pNAIM,:pPRODOLJ,:pSUMMA,:pNAPRAV);
END
/* теперь удаление... */
DELETE FROM MP WHERE N_TEL = :pN_TEL; -- удаляем записи из МП
DELETE FROM raschet WHERE N_TEL = :pN_TEL; -- удаляем записи из табл.расчета
/* Вносим новый год данные в таблицу расчетов */
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/01', 'Январь', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/02', 'Февраль', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/03', 'Март', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/04', 'Апрель', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/05', 'Май', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/06', 'Июнь', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/07', 'Июль', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/08', 'Август', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/09', 'Сентябрь', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/10', 'Октябрь', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/11', 'Ноябрь', :pYear);
INSERT INTO raschet (N_TEL, G_M, MESYAC, GOD) VALUES (:pN_tel, :pYear||'/12', 'Декабрь', :pYear);
IF (:ps>0) then -- вносим долг или аванс с конца прошлого года
update raschet
set DOLG_N = :ps;
ELSE
update raschet
set AVANS_N = -:ps;
EXECUTE PROCEDURE after_raschet_upd(:pN_TEL);
END
END
Column does not belong to referenced table.
Dynamic SQL Error.
SQL error code = -206.
Subselect illegal in this context
Вы думаете у всех есть большое желание разбираться в вашем коде и искать, где же у вас там ошибка? Для начала локализуйте место ошибки, чтобы было о чем говорить.
Просмотрев код, могу дать лишь один комментарий. Вот из Вашего примера цикл
FOR SELECT * FROM mp WHERE N_TEl = :pN_TEL INTO :pTEL,
:pG_M,
:pDATA,
:pVREMYA,
:pNAIM,
:pPRODOLJ,
:pSUMMA,
:pNAPRAV
DO -- перекидываем данные из МП в арх.МП
BEGIN
INSERT INTO archive_mp
VALUES (:pN_PP,:pN_TEL,:pG_M,:pDATA,:pVREMYA,:pNAIM,:pPRODOLJ,:pSUMMA,:pNAPRAV);
END
...
Неужели его нельзя заменить одним запросом вида INSERT-SELECT? Зачем цикл? Этож тормозит очень... опять же избавитесь от одной конструкции FOR SELECT DO внутри основного цикла.
Удачи