powered by simpleCommunicator - 2.0.60     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Sybase ASA, ASE, IQ [игнор отключен] [закрыт для гостей] / Еще разок про LOCK TABLE
8 сообщений из 8, страница 1 из 1
Еще разок про LOCK TABLE
    #33793445
wados
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Буду краток, есть вот такая СП, которая списывает с остатка(Ostatok) в содержание расхода(Rashod)
Код: 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.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
CREATE PROCEDURE "DBA"."Rashod"(in @Kol decimal( 10 , 3 ), 
                        in @TovarNo integer, 
                        in @PriceOut decimal( 10 , 2 ),
                        in @NaklNo integer, 
                        in @SkladNo integer)
BEGIN ATOMIC
declare @OstatokKol decimal( 10 , 3 );
declare @OstatokNo unsigned int;
//
declare err_notfound exception for sqlstate value '02000';
declare CurOstatok dynamic scroll cursor for select OstatokNo,OstatokKol from #TmpOstatok order by OstatokNo asc;
CREATE TABLE #TmpOStatok(
	OstatokNo unsigned int,
	OstatokKol decimal ( 10 , 3 ));
// Лочим таблицу чтобы никто из писателей не влез
lock table Ostatok IN SHARE MODE;
//
// получаем остатки по товару по складу kol>0
insert into #TmpOStatok(OstatokNo,OstatokKol)
                 select OstatokNo,OstatokKol 
                 from Ostatok
                 where OstatokTovarNo=@TovarNo and OstatokSkladNo=@SkladNo and OstatokKol> 0 ;
//
if (@Kol< 0 )or(@Kol>isnull((select sum(OstatokKol) from #TmpOStatok), 0 )) 
   then
    RAISERROR  20000  'На остатке нет такого количества,или отрицательное количество';
   else   
   open CurOstatok;
   CurLoop: loop
   fetch next CurOstatok into @OstatokNo,@OstatokKol;
   if (sqlstate = err_notfound)or(@Kol= 0 ) then leave CurLoop end if;   
   if @Kol>=@OstatokKol then // можно списать строку в остатках в "0"
      set @Kol=@Kol-@OstatokKol;
      Update Ostatok set OstatokKol= 0  where OstatokNo=@OstatokNo;
      insert into Rashod(RashodNaklNo,RashodKol,RashodPrice,RashodTovarNo,RashodOstatokNo)
      values(@NaklNo,@OstatokKol,@PriceOut,@TovarNo,@OstatokNo);
   else                     // можно спмсать количество в "0"
      Update Ostatok set OstatokKol=@OstatokKol-@Kol where OstatokNo=@OstatokNo;
      insert into Rashod(RashodNaklNo,RashodKol,RashodPrice,RashodTovarNo,RashodOstatokNo)
      values(@NaklNo,@Kol,@PriceOut,@TovarNo,@OstatokNo);
      set @Kol= 0 ;
   end if;
   end loop CurLoop;
   close CurOstatok;
   if @Kol> 0  then 
    RAISERROR  20000  'Не удалось списать все количество! Ошибка!' 
   end if;
end if;
END
ВОПРОСЫ:
1. Самое главное, отпустит ли залоченную таблицу, процедура атомик, на коммиты ругается (и хелпа вместе с ним). Начитался, что вроде как для того чтобы отпустило нужен комит... Autocommit пройдет автоматом сразу?
2. По ошибке (RAISERROR) опять же - отпустит ли залоченную таблицу?
...
Рейтинг: 0 / 0
Еще разок про LOCK TABLE
    #33793542
Фотография ASCRUS
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Тоже буду краток:
никто ничего не отпустит, пока не будет вызван явный COMMIT или ROLLBACK.

P.S. Процедура вызывается в контексте производящейся транзакции или на момент ее запуска никто не стартовал транзакцию ?
--
www.rusug.ru - портал русскоязычной группы пользователей Sybase
...
Рейтинг: 0 / 0
Еще разок про LOCK TABLE
    #33793990
wados
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Нет, транзакция явно не стартуется....наиболее простой вариант наверно так:
Код: plaintext
1.
2.
3.
4.
5.
begin
begin atomic
.....................
end;
commit;
end;
и тут вопрос на пол миллиона: сколько надо кoмитов? и как отпуститься по raiserror...
...
Рейтинг: 0 / 0
Еще разок про LOCK TABLE
    #33794091
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Вот как раз одна из тех задач из-за которых мы спорили на rusug :)

На таблицу остатка, делаем ограничение:
Код: plaintext
alter table Ostatok modify OstatokKol ... check(OstatokKol>= 0 );
делаем триггер на таблицу расхода:
Код: plaintext
1.
2.
3.
create trigger tr_rashod after insert, update on Rashod new as new_row for each row
begin
   update Ostatok set OstatokKol = OstatokKol - new_row.RashodKol where OstatokNo=new_row.RashodOstatokNo;
end;
И все, никаких хранимых процедур, никаких RAISERROR уже не нужно. Обновляем таблицу расходов, и если остаток обновится не смог - сервер сам выдаст ошибку.


---
http://www.rusug.ru] Портал рускоязычной группы пользователей Sybase
...
Рейтинг: 0 / 0
Еще разок про LOCK TABLE
    #33794225
Фотография ASCRUS
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну во первых мы на RUSUG спорили из за целесообразности применения RAISERROR в случаях, когда сложная бизнес логика должна вернуть внятную ошибку клиенту, прервав весь процесс выполнения транзакции.

Во вторых не вижу смысла в CHECK, если поле остатка имеет тип unsigned int - у меня именно так все и работает, причем замечательно работает.

В третьих у меня к примеру есть процедуры, которые не смотря на такие проверки должны проверить наличие товара на складе ДО его физического расхода в целях уменьшения кол-ва блокировок при больших нагрузках на БД, где опять же идеально подходит тот же самый RAISERROR ;)

wados
Я вот упорно не понимаю, зачем Вам обязательно нужен атомарный блок BEGIN ATOMIC, особенно с учетом того, что транзакция стартует в ХП, завершается в ХП и никаких точек отката особо не нужно. Мне кажется Вы явно используете его не по назначению, ассоциируя его с транзакцией, где на самом деле это просто более простая форма записи точки сохранения части транзакции (SAVEPOINT).

По сабжу малость покритикую:
1. BEGIN ATOMIC не нужен.
2. Обьявлены исключения, которые нигде не используются.
3. Совершенно не понимаю логики с времянкой и курсором, зачем это нужно, когда все можно сделать одним UPDATE.
4. Если и пользоваться курсорами, то гораздо удобнее и выразительнее их обьявлять через FOR.
5. Чтобы RAISERROR прерывала выполнение процедуры, необходимо установить опцию БД CONTINUE_AFTER_RAISERROR в OFF.
6. Раз транзакция начинается и заканчивается в ХП, то в конце должен стоять блок EXCEPTION, который как раз перехватывает исключение, сгенерированное через SIGNAL, делает ROLLBACK и потом через RAISERROR возвращает клиенту текст ошибки (вот здесь как раз в парочке SIGNAL в коде ХП и RAISERROR в обработчике ошибок EXCEPTION смотрятся замечательно).
7. В ASA есть возможность сделать собственную библиотеку ошибок через CREATE MESSAGE, с поддержкой параметров, вставляемых в текст, где достаточно вызвать RAISEERROR КодОшибки, параметр1, параметр2, ...

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

--
www.rusug.ru - портал русскоязычной группы пользователей Sybase
...
Рейтинг: 0 / 0
Еще разок про LOCK TABLE
    #33794257
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ASCRUSНу во первых мы на RUSUG спорили из за целесообразности применения RAISERROR в случаях, когда сложная бизнес логика должна вернуть внятную ошибку клиенту, прервав весь процесс выполнения транзакции.Ну? А здесь разве другой случай? Здесь задача вообще элементарная. Тут ручные исключения даже отдаленно не нужны.

ASCRUSВо вторых не вижу смысла в CHECK, если поле остатка имеет тип unsigned int - у меня именно так все и работает, причем замечательно работает.Тоже верно :) Сменить тип поля, повесить триггер и жить станет легко и замечтельно.

ASCRUSВ третьих у меня к примеру есть процедуры, которые не смотря на такие проверки должны проверить наличие товара на складе ДО его физического расхода в целях уменьшения кол-ва блокировок при больших нагрузках на БД, где опять же идеально подходит тот же самый RAISERROR ;)эээээ... мы говорим сейчас про задачу списания остатков? В ней raiseerror никак не уменьшит количество блокировок по сравнению с триггером и check (или беззнаковым типом данных :))

---
www.rusug.ru - портал русскоязычной группы пользователей Sybase
...
Рейтинг: 0 / 0
Еще разок про LOCK TABLE
    #33795094
wados
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Прошу прощения что спорю с метрами...но чуток обьясню.

структура остатка:
OstatoNo unsigned int global autoinc
Kol decimal(10,3)
TovarNo int
PK(OstatoNo)

Структура содержания расхода:
RashodNo unsigned int global autoinc
NaklNo int
Kol decimal(10,3)
TovarNo int
PK(RashodNo)

так вот, на остатках товар может лежать в разных строчках с разным соответсвенно OstatoNo (который так же привязан в приход для поднятия нужной прих. накладной). таким образом, при расходе если количество расходуемого товара превышает кол товара в одной строке в остатке то в остатке по этой строке списывается в 0 и затем продолжается списание со след строки и при этом формируется НОВАЯ строка расхода, может чуток мудрено конечно...

ЗАЛОЧИТЬ остаки
Получить остатки по товару
проверить если списываемое количество "-" или БОЛЬШЕ чем есть на остатке то ОШИБКА
ЦИКЛ
получить строку из остаков по товару
если списываемое количество=0 или конец остатков то выход из цикла
если количество на остаке меньше списывемого ТОГДА списать остаки в ЭТОЙ строке в "0", зенести в расход строку с количеством из остаков и кодом строки остатка, уменьшить списываемое количество на количество из остаков.
ИНАЧЕ уменьшить остатки на списываемое количество, занести в расход строку со списываемым количеством и кодом строки остатка, списываемое количество обнулить.
КОНЕЦ цикла
таким образом в расходе будет столько строк сколько наберется из остатка по суммарному количеству.

БОЛЬШОЕ СПАСИБО за замечания.
триггером на расход не получится, получится только повесить триггер на обновление кол в расходе по строке с обновление остатков. через FOR была и у меня мысль, если бы не проверка по суммарному количеству.
Я думаю мне должно подойти то что написано по теме "Проблема с транзакциями в ASA", касательно SavePoint. касательно EXCEPTION нагнал беса...
Опцию БД не проверял, надо проверить
ВЫЧИТАЛ:
No locks are released by the RELEASE SAVEPOINT or ROLLBACK TO SAVEPOINT commands: locks are released only at the end of a transaction .
вот такие пирожки с котятами... ROLLBACK TO SAVEPOINT НЕ СНИМАЕТ LOCK!!!
насчет ROLLBACK TRANSACTION и LOCK ничего не написано....
таким образом, ПОЛЬЗОВАТЬСЯ НАДО BEGIN TRAN T1 ... COMMIT TRAN T1.
...
Рейтинг: 0 / 0
Еще разок про LOCK TABLE
    #33796476
White Owl
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
wadosПрошу прощения что спорю с метрами...но чуток обьясню. С метрами спорить бессмысленно, метры можно только измерять рулеткой :)

wadosпри расходе если количество расходуемого товара превышает кол товара в одной строке в остатке то в остатке по этой строке списывается в 0 и затем продолжается списание со след строки и при этом формируется НОВАЯ строка расхода, может чуток мудрено конечно...Тогда да, тогда без циклов не обойтись. А зачем так сложно? Или это попытка учитывать остатки по отдельным приходам?
...
Рейтинг: 0 / 0
8 сообщений из 8, страница 1 из 1
Форумы / Sybase ASA, ASE, IQ [игнор отключен] [закрыт для гостей] / Еще разок про LOCK TABLE
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


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