Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

То ли глюк, то ли... непонятно...

23K
08 января 2008 года
Gluckodrom
30 / / 08.01.2008
Код:
SET TERM ^;
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.
*************************************
Устал уже с ним воевать...
412
09 января 2008 года
grgdvo
323 / / 04.07.2007
Судя по документации http://www.ibphoenix.com/downloads/60LangRef.zip вы неверно используете SELECT в конструкции FOR SELECT ... 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 в первом цикле.

23K
09 января 2008 года
Gluckodrom
30 / / 08.01.2008
спс огромное!!! Понял в чем ошибочка моя. Еще раз спс!
23K
09 января 2008 года
Gluckodrom
30 / / 08.01.2008
цикл не до конца готов. Дело в том, что кажое следующее значение зависит от значения предыдущего. Пример: расчет обслуживания абонентов, т.е. абон.плата - оплата = долг на конец месяца и, соответсвенно, этот долг является долгом на начало следущего месяца.
23K
12 января 2008 года
Gluckodrom
30 / / 08.01.2008
натолкнулся на другую проблему... триггер после обновления таблицы "расчет", но в самом триггере происходит обновление таблицы "расчет". Получается зацикливание...
Подскажите, кто знает, как сделать чтобы триггер вызывался только один раз за транзакцию. или какой-нить другой вариант решения.

Код:
SET TERM ^;
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
8.2K
12 января 2008 года
Ora-cool
211 / / 20.09.2007
Цитата: Gluckodrom

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


Отказаться от триггера, а написать хранимую процедуру, в которой реализовать всю необходимую логику

1
12 января 2008 года
kot_
7.3K / / 20.01.2000
Цитата: Ora-cool
Отказаться от триггера, а написать хранимую процедуру, в которой реализовать всю необходимую логику


мммм. Я бы посоветовал использовать замещающий триггер - не

 
Код:
AFTER UPDATE

а
 
Код:
INSTEAD OF UPDATE

если конечно в птице это реализовано (не помню)
Все таки разница между триггером и ХП достаточно значительна.
8.2K
13 января 2008 года
Ora-cool
211 / / 20.09.2007
Цитата: kot_
мммм. Я бы посоветовал использовать замещающий триггер - не
 
Код:
AFTER UPDATE

а
 
Код:
INSTEAD OF UPDATE

если конечно в птице это реализовано (не помню)
Все таки разница между триггером и ХП достаточно значительна.



В FB насколько я знаю можно создавать триггеры на VIEW (не помню правда начиная с какой версии сервера), может через них можно извернуться, но я бы все таки лучше сделал через хранимку. Т.е. отказаться от прямого update таблицы, а вместо этого вызывать ХП, в которой реализована вся бизнес-логика.

1
13 января 2008 года
kot_
7.3K / / 20.01.2000
Цитата: Ora-cool
В FB насколько я знаю можно создавать триггеры на VIEW (не помню правда начиная с какой версии сервера), может через них можно извернуться, но я бы все таки лучше сделал через хранимку. Т.е. отказаться от прямого update таблицы, а вместо этого вызывать ХП, в которой реализована вся бизнес-логика.


Во второй уже должно быть реализовано (по крайней мере обещали :))
Все это верно - но дело в том что апдатиться таблица не обязательно на прямую - как правило это всегда делается через ХП (я по крайней мере всегда делаю так) - а использование триггера как правило необходимо для разделения бизнес-логики и "низкоуровневой" логики. В качестве примера, можно привести установку флага активности для последней вставленной записи - ИМХО, это уровень не бизнес-логики (которая реализуется в ХП) и более правильно для этого использовать именно триггер.

8.2K
13 января 2008 года
Ora-cool
211 / / 20.09.2007
Цитата: kot_

Все это верно - но дело в том что апдатиться таблица не обязательно на прямую - как правило это всегда делается через ХП (я по крайней мере всегда делаю так) - а использование триггера как правило необходимо для разделения бизнес-логики и "низкоуровневой" логики. В качестве примера, можно привести установку флага активности для последней вставленной записи - ИМХО, это уровень не бизнес-логики (которая реализуется в ХП) и более правильно для этого использовать именно триггер.


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

23K
13 января 2008 года
Gluckodrom
30 / / 08.01.2008
спс за ответы. Пришлось сделать через отдельную хранимую процедуру.
Еще один вопрос: можно ли обращаться к полям таблицы не по имени, а по номеру. Если можно, то какаая конструкция?
8.2K
13 января 2008 года
Ora-cool
211 / / 20.09.2007
Цитата: Gluckodrom
спс за ответы. Пришлось сделать через отдельную хранимую процедуру.
Еще один вопрос: можно ли обращаться к полям таблицы не по имени, а по номеру. Если можно, то какаая конструкция?



Только недавно такая тема была:
http://forum.codenet.ru/showthread.php?t=44192
В общем случае нельзя.

23K
15 января 2008 года
Gluckodrom
30 / / 08.01.2008
никак не могу, понять почему ругается на подвыборки... :confused:
Код:
SET TERM ^;
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
8.2K
15 января 2008 года
Ora-cool
211 / / 20.09.2007
Цитата: Gluckodrom
никак не могу, понять почему ругается на подвыборки... :confused:


Вы думаете у всех есть большое желание разбираться в вашем коде и искать, где же у вас там ошибка? Для начала локализуйте место ошибки, чтобы было о чем говорить.

412
16 января 2008 года
grgdvo
323 / / 04.07.2007
Да... эдак Вы уже на шею залезли и ноги свесили :) без номера строки вручную ошибку найти нереально... участники форума - обычные люди :)

Просмотрев код, могу дать лишь один комментарий. Вот из Вашего примера цикл

Цитата: Gluckodrom

Код:
....
  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 внутри основного цикла.

Удачи

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог