Страница 1 из 1

Ошибка в select ... into ... или я чего-то не понимаю ?

Добавлено: 02 фев 2006, 16:23
noisy
Не буду приводить весь текст процедуры, объясню на словах.
есть процедура расчитывающая начисления абоненту с учетом скидок на данную услуг, если нет скидки на данную услугу, проверяем нет ли общей скидки на все услуги.

так вот

Код: Выделить всё

for
  select S.CUSTOMER_ID, count(DT.T_DAY), sum(DT.TARIF)
  from SUBSCR_HIST S, DAYS_TARIF DT
  where S.SERV_ID = :V_SERVICE_ID
    and DT.SERV_ID = S.SERV_ID
    and DT.T_DAY >= :V_S_MONTH
    and DT.T_DAY <= :V_E_MONTH
    and DT.T_DAY between S.DATE_FROM and S.DATE_TO
  group by  1
  into :CUSTOMER_ID, :V_UNITS, :V_FEE
do begin
  select D.FACTOR_VALUE
  from DISCOUNT_FACTOR D
  where D.CUSTOMER_ID = :CUSTOMER_ID
    and :V_S_MONTH between D.DATE_FROM and D.DATE_TO
    and (D.SERV_ID = :V_SERVICE_ID)
  into :V_DISCOUNT;
  if (V_DISCOUNT is null)
  then select D.FACTOR_VALUE
  from DISCOUNT_FACTOR D
  where D.CUSTOMER_ID = :CUSTOMER_ID
    and :V_S_MONTH between D.DATE_FROM and D.DATE_TO
    and (D.SERV_ID = -1)
  into :V_DISCOUNT;
  if (V_DISCOUNT is not null) then V_FEE = V_FEE * V_DISCOUNT;
  V_FEE = ROUND(V_FEE,V_FEE_ROUND);
  insert into MONTHLY_FEE(MONTH_ID, CUSTOMER_ID, SERVICE_ID, UNITS, FEE, EXPENSE_TYPE)
  values (:V_S_MONTH, :CUSTOMER_ID, :V_SERVICE_ID, :V_UNITS, :V_FEE, :V_EXPENSE_TYPE);
end
не верно расчитывает скидки (точнее всем ставит коэфф. 0,5)

а вот этота процедура считает верно.

Код: Выделить всё

for
  select S.CUSTOMER_ID, count(DT.T_DAY), sum(DT.TARIF)
  from SUBSCR_HIST S, DAYS_TARIF DT
  where S.SERV_ID = :V_SERVICE_ID
    and DT.SERV_ID = S.SERV_ID
    and DT.T_DAY >= :V_S_MONTH
    and DT.T_DAY <= :V_E_MONTH
    and DT.T_DAY between S.DATE_FROM and S.DATE_TO
  group by  1
  into :CUSTOMER_ID, :V_UNITS, :V_FEE
do begin
  for 
    select D.FACTOR_VALUE
    from DISCOUNT_FACTOR D
    where D.CUSTOMER_ID = :CUSTOMER_ID
      and :V_S_MONTH between D.DATE_FROM and D.DATE_TO
      and (D.SERV_ID = :V_SERVICE_ID)
    into :V_DISCOUNT
  do if (V_DISCOUNT is not null) then V_FEE = V_FEE * V_DISCOUNT;
  if (V_DISCOUNT is null)
  then begin
    for
      select D.FACTOR_VALUE
      from DISCOUNT_FACTOR D
      where D.CUSTOMER_ID = :CUSTOMER_ID
        and :V_S_MONTH between D.DATE_FROM and D.DATE_TO
        and (D.SERV_ID = -1)
      into :V_DISCOUNT
    do if (V_DISCOUNT is not null) then V_FEE = V_FEE * V_DISCOUNT;
  end
  V_FEE = ROUND(V_FEE,V_FEE_ROUND);
  insert into MONTHLY_FEE(MONTH_ID, CUSTOMER_ID, SERVICE_ID, UNITS, FEE, EXPENSE_TYPE)
  values (:V_S_MONTH, :CUSTOMER_ID, :V_SERVICE_ID, :V_UNITS, :V_FEE, :V_EXPENSE_TYPE);
end
т.е. завернув запрос select ... into ... в структуру for ... do ...
спасает ситуацию!?

проверял на FB 1.5.2 и FB 1.5.3....
да.. еще интересный момент.. при отладке в IBexpert превый текст процедуры работает верно!

Добавлено: 02 фев 2006, 17:26
kdv
ты хоть бы сказал, чем эти процедуры отличаются. А то в код вникать совсем неинтересно.
т.е. завернув запрос select ... into ... в структуру for ... do ...
спасает ситуацию!?
приплыли. for select - это для перебора записей, если запрос может вернуть > 1 записи. А select без for - только если запрос возвращает 0 или 1 запись.
В обоих случаях, если запрос НЕ возвращает ни одной записи, то переменные INTO НЕ МЕНЯЮТСЯ. То есть, они остаются как были до того.
да.. еще интересный момент.. при отладке в IBexpert превый текст процедуры работает верно!
IBExpert "отлаживает" процедуры разбивая их на sql, и самостоятельно обрабатывает циклы и множество других вещей. Так что...

я бы тебе посоветовал проверить присвоение V_DISCOUNT.

вот это вот:

Код: Выделить всё

if (V_DISCOUNT is null)
сработает если
1. запрос вернул запись с D.FACTOR_VALUE, содержащим null
2. запрос не вернул ни одной записи, и D.FACTOR_VALUE БЫЛ null перед вызовом этого запроса.

p.s.

Код: Выделить всё

if Records > 0 then
   B = 5;
так вот. Если Records = 0, то каким будет B ?

Добавлено: 02 фев 2006, 17:56
noisy
да...
поставил перед селектом

Код: Выделить всё

V_DISCOUNT = null
и все заработало....

а я то думал, что если запрос ничего не вернул, то переменная будет null

спасибо, kdv

Добавлено: 02 фев 2006, 18:01
Merlin
kdv писал(а):ты хоть бы сказал, чем эти процедуры отличаются. А то в код вникать совсем неинтересно.
т.е. завернув запрос select ... into ... в структуру for ... do ...
спасает ситуацию!?
приплыли.
:lol: В данном конкретном случае таки спасает :lol: Он втащил умножение на скидку в цикл, и поэтому если ничего не найдено, то не только не меняется значение скидки, но и не выполняется умножение. Но оригинальненько, надо сказать, оргинальненько, вместо зануления переменной первым оператором цикла :)

Добавлено: 03 фев 2006, 09:44
noisy
в данной ситуации for ... do ... делает еще одно доброе дело...
если в таблице скидок несколько записей скидок на услугу, то при просто выборке select ... into ... произойдет ошибка, а for... do/// переберет все коэффициенты :)

Добавлено: 03 фев 2006, 09:46
kdv
тогда это не "доброе дело", а просто некорректный код. я имею в виду singleton select вместо for select, если несколько записей возвращается.

Добавлено: 03 фев 2006, 10:21
noisy
Согласен... но до этого я дошел только после того как начал вылавливать ошибку.

Добавлено: 03 фев 2006, 12:37
Merlin
noisy писал(а):в данной ситуации for ... do ... делает еще одно доброе дело...
если в таблице скидок несколько записей скидок на услугу, то при просто выборке select ... into ... произойдет ошибка, а for... do/// переберет все коэффициенты :)
Ой. И какую же скидку в итоге сделает? Из множества-то? ;)

Добавлено: 03 фев 2006, 17:04
noisy
Merlin писал(а):
noisy писал(а):в данной ситуации for ... do ... делает еще одно доброе дело...
если в таблице скидок несколько записей скидок на услугу, то при просто выборке select ... into ... произойдет ошибка, а for... do/// переберет все коэффициенты :)
Ой. И какую же скидку в итоге сделает? Из множества-то? ;)
так это пусть потом они сами смотрят, что там им насчитает, все ж лучше чем ошибка при выполнении программы!

кстати, вопрос а можно ли проверить в триггере пересекаются ли диапазоны дат?
т.е. при вставке в таблицу вида

Код: Выделить всё

CREATE TABLE DISCOUNT_FACTOR (
    DISCOUNT_ID   INTEGER NOT NULL,
    CUSTOMER_ID   INTEGER NOT NULL,
    DATE_FROM     DATE NOT NULL DEFAULT 'NOW',
    DATE_TO       DATE NOT NULL DEFAULT 'NOW',
    FACTOR_VALUE  NUMERIC(15,2) NOT NULL,
    SERV_ID       INTEGER NOT NULL DEFAULT -1,
    NOTICE        VARCHAR(255) 
);
проверить есть ли на такого абонента скидка и не попадаем ли мы в уже имеющийся диапазон дат?