Форум: "Базы";
Текущий архив: 2003.06.19;
Скачать: [xml.tar.bz2];
ВнизРаботаем с IB по сети Найти похожие ветки
← →
xmrz (2003-05-27 00:22) [0]Уважаемые мастера, помогите пожалуйста разобраться.
БД Yaffil на сервере, с ней работают 2 клиента, в основном вставка данных в таком виде :
/
SELECT ID FROM AUTOM WHERE (VIN = :VIN) AND (MODEL = :MODEL) INTO AID;
INSERT INTO DOC_POLICE (DATA,WHO_INS,SUMMA,OPERATION) VALUES (:INDATA,:INS,:SUMDAY,1);
SELECT MAX(ID) FROM DOC_POLICE WHERE (OPERATION=1) AND (WHO_INS=:INS) INTO PID;
INSERT INTO INOUT (IO_MOMENT,DATA,WHO_INS,FIO,AUTOM,OPERATION,DOC_ID)
VALUES (:MOMENT,:INDATA,:INS,:FIO,:AID,1,:PID);
\
В таблицах ID генерируются с помощью триггеров и генераторов BEFORE INSERT.
Таблицы - как видно имеют зависимости : ID из AUTOM и DOC_POLICE подставляются в INOUT.
Выполнение всех вышеперечисленных операций вызывается из одной процедуры, после вызова
процедуры клиентом сразу делается Transaction.Commit.
Опция ForcedWrites базы была выключена. Через какое-то время стали прооисходить сбои при
отключении эл. энергии, причём сервер не выключался, только клиенты. Сбои заключались в том, что
после возобновления питания в таблице INOUT записи вставленные с разных клиентов имели
один и тот же ID из табл. DOC_POLICE. А в DOC_POLICE оказывались 2 одинаковых ID, хотя по этому полю
создан уникальный индекс!
После включения ForcedWrites при сбоях с питанием таких вещей вроде не происходит, но зато безо всяких
на то причин стали появляться "пропуски" в таблицах DOC_POLICE и INOUT, т.е. триггер генерирует значения
вхолостую. Если бы я где то делал RollBack или где то во время выполнения ХП выскакивали Access Violation...
было бы всё понятно... В чём может быть причина, поделитесь пожалуйста своими соображениями.
← →
Alexandr (2003-05-27 06:02) [1]1) Force Writes рулит. А вообще, интересует версия Yaffil
2) Пропуски из-за того, что генераторы вне транзакций выполняются. И соответственно не откатываются.
← →
xmrz (2003-05-27 08:43) [2]В том то и дело, что я ничего явно не откатываю, и по идее не должны генераторы работать вхолостую! Я использую yaffil classic server 868 сборки.
← →
Alexandr (2003-05-27 08:53) [3]по чьей идее?
1) Как только ты делаешь
gen_id(gen_name,1)
значение генератора сразу увеличивается на 1.
Независимо ниотчего...
2) Не дело это вообще-то следить за инкрементом генератора. Он для этого не предназначен. Он гарантирует выдачу уникальных значений. Но никак не последовательных.
3) RollBack может происходить автоматически при обрыве соединения или ты можешь получать значение генератора где-то лишний раз, или получив забыть про это, или получить, а потом оно не нужно... Отсюда и пропуски.
← →
xmrz (2003-05-27 09:07) [4]Генератор срабатывает только BEFORE INSERT. Хранимая процедура выполняется на сервере, причём помимо INSERT процедура включает проверки которые исключают появление ошибок при вставке данных. После выполнения запроса клиент НЕ ВЫРУБАЕТСЯ и корректно делает commit!
А как насчёт появления дублирующихся значений в котором стоит UNIQUE INDEX?
По моему что-то не вяжется.
← →
Alexandr (2003-05-27 09:23) [5]нет... это у тебя в голове не вяжется.
1) приведи тут свою ХП
2) А клиент не может передумать вставлять запись после того, как уже вставил?
3) Про дублирующиеся значения: а не надо было питание у сервака вырубать, или Force Writes поставить надо было.
К тому, же до сих пор я не увидел версии сервера БД
← →
xmrz (2003-05-27 09:28) [6]1)
CREATE PROCEDURE "AUTOIN"
( FIO VARCHAR (40),
VIN VARCHAR (17),
MODEL VARCHAR (40),
INS INTEGER,
INDATA INTEGER,
COLOR VARCHAR (40),
MOMENT TIMESTAMP,
ATYPE VARCHAR (1))
RETURNS
( PID INTEGER)
AS
DECLARE VARIABLE AID INTEGER;
DECLARE VARIABLE AID2 INTEGER;
DECLARE VARIABLE SUMDAY DOUBLE PRECISION;
DECLARE VARIABLE ISBIG VARCHAR (1);
BEGIN
SELECT ID FROM AUTOM WHERE ((VIN like qstr(:VIN)) or (POS(VIN,:VIN)>0)) AND (MODEL = :MODEL) INTO AID;
SELECT SUMMA FROM MAIN WHERE ID=:INDATA INTO SUMDAY;
IF (:AID IS NOT NULL) THEN
BEGIN
SELECT ATYPE FROM AUTOM WHERE (ID = :AID) INTO ISBIG;
IF (:ISBIG="1") THEN BEGIN SUMDAY = SUMDAY*1.5; END
INSERT INTO DOC_POLICE (DATA,WHO_INS,SUMMA,OPERATION) VALUES (:INDATA,:INS,:SUMDAY,1);
SELECT MAX(ID) FROM DOC_POLICE WHERE (OPERATION=1) AND (WHO_INS=:INS) INTO PID;
INSERT INTO INOUT (IO_MOMENT,DATA,WHO_INS,FIO,AUTOM,OPERATION,DOC_ID)
VALUES (:MOMENT,:INDATA,:INS,:FIO,:AID,1,:PID);
UPDATE AUTOM
SET ISIN = "1"
WHERE ID = :AID;
EXECUTE PROCEDURE MAIN_IN (:INDATA);
SUSPEND;
END
ELSE
BEGIN
INSERT INTO AUTOM (VIN,MODEL,WHO_INS,ISIN,COLOR, ATYPE) VALUES (:VIN, :MODEL, :INS,"1",:COLOR, :ATYPE);
SELECT ID FROM AUTOM WHERE (VIN = :VIN) AND (MODEL = :MODEL) INTO AID;
IF (:ATYPE="1") THEN BEGIN SUMDAY = SUMDAY*1.5; END
INSERT INTO DOC_POLICE (DATA,WHO_INS,SUMMA,OPERATION) VALUES (:INDATA,:INS,:SUMDAY,1);
SELECT MAX(ID) FROM DOC_POLICE WHERE (OPERATION=1) AND (WHO_INS=:INS) INTO PID;
INSERT INTO INOUT (IO_MOMENT,DATA,WHO_INS,FIO,AUTOM,OPERATION,DOC_ID)
VALUES (:MOMENT,:INDATA,:INS,:FIO,:AID,1,:PID);
EXECUTE PROCEDURE MAIN_IN (:INDATA);
SUSPEND;
END
END^
SET TERM ; ^
/*SELECT * FROM AUTOIN ("ИВАНОВ С.В.","XTARGGWGWG","ВАЗ",1,5,"КРАСНЫЙ");
SELECT * FROM AUTOIN ("Петров В.В.","1134567","Volvo",1,1);*/
2) После нажатия применить для клиента всё потеряно..., происходит Query.Active := true; Query.Active := false; ...Commit;
3) Питание сервера не вырубаетя: стоит на ИБП, + резерв - дизель генератор который запускается в течении 1 мин.
4) yaffil classic server 868 сборки.
← →
Alexandr (2003-05-27 09:36) [7]1) а где получение значения генератора?
2) когда притание клиентов вырубится, то сервер откатывает транзакции клиентов
а вообще, я уже все сказал по данному вопросу.
А как там у тебя программа написано, мне отсюда не видно.
← →
Danilka (2003-05-27 09:37) [8]гы, может я глючу, но в упор не вижу где здесь дергается генератор.
даже поиск по gen_id делал...
← →
xmrz (2003-05-27 09:46) [9]1) Генераторы получают новые значения автоматически при вставке новой записи:
SET TERM ^ ;
CREATE TRIGGER SET_INOUT_ID FOR INOUT
BEFORE INSERT AS
BEGIN
NEW.ID = GEN_ID(INOUT_GEN, 1);
END^
SET TERM ;^
2) Питание на тот момент не выключалось. Я слежу за временем, когда выполняются операции в IO_MOMENT
← →
Alexandr (2003-05-27 09:55) [10]не бывает так.
ну не может генератор сам увеличиться...
← →
xmrz (2003-05-27 10:12) [11]> Alexandr © (27.05.03 09:55)
Я и сам сначала подумал про питание, но всвязи с тем что имею возможность реально следить за временем понимаю что искать ошибку надо в другом месте. Поэтому и решил обратиться за помощью к людям :)
А если теоретически предположить что запросы от двух клиентов пришли одновременно? Процедура ведь тяжёлая, UDF используются?
Ну хоть какие нибудь идеи?!
← →
Alexandr (2003-05-27 10:16) [12]ну и что. Все равно нормально будет.
а главная идея - не закладываться вообще на поледовательность значений формируемых генератором и принять пропуски как должное.
Да и вот еще. Если процедуру вызываешь на клиенте и спользуешь IBX , то возможен при определенных обстоятельствах двукратный вызов процедуры.
← →
xmrz (2003-05-27 10:25) [13]Что сможет решить эту проблему в принципе? Переход на трёхзвенную сруктуру? Кусок клиента отвечающий за запуск процедур вынести на сервер? Не работал раньше с Midas, даже страшно как то... :(
← →
Alexandr (2003-05-27 10:33) [14]какую проблему?
← →
Danilka (2003-05-27 10:33) [15]xmrz (27.05.03 10:25)
Два раза уже сказал Alexandr ©, скажу третий раз: не надо привязываться к значениям генератора. Это не его задача.
← →
xmrz (2003-05-27 10:38) [16]Тогда может порекомендуете механизм гарантирующий генерацию последовательных номеров? Использовать select Max(ID) ...?
← →
Alexandr (2003-05-27 10:40) [17]по разному можно в зависимости от задачи и от необходимости
← →
Danilka (2003-05-27 10:42) [18]xmrz (27.05.03 10:38)
Вообще-то гарантированая последовательность номеров нужна только юзерам, в любых других случаях можно обойтись. Пусть они этим и занимаются. :))
А Max(ID) - очень вероятен глюк на нескольких пользователях, когда два или больше юзеров сделали max(id) в транзакциях, стартовавших примерно в одно время.
← →
xmrz (2003-05-27 10:47) [19]Проблема возникла именно в пик работы операторов. Так что на select Max(ID) .. надеяться особо не приходится
← →
Danilka (2003-05-27 10:49) [20]xmrz (27.05.03 10:47)
а когда нужна эта гарантированая последовательность?
если для отчетов, то ее можно генерировать во время отчета.
честно говоря, не знаю, в остальном чем могут помешать пропуски.
← →
Zacho (2003-05-27 10:59) [21]
> xmrz (27.05.03 10:47)
Если нужна автоматически генерируемая последовательность номеров без пропусков, то сделать это можно следующим образом:
Стартуем транзакцию. При созданием нового документа блокируем таблицу (в том числе на чтение). Получаем MAX(ID)+1. Создаем документ, INSERT, Commit.
Естественно, редактировать номера и удалять документы - запрещаем.
Недостаток: это практически "однопользовательский" режим работы. При интенсивной работе нескольких операторов возможны приличные торможения. Задержки можно свести к минимуму, если получать номер непосредственно перед вставкой, в как можно более короткой транзакции.
← →
xmrz (2003-05-27 10:59) [22]Дело в том, что с этими номерами тут же распечатываются квитанции. "Покупатель" уходит с квитанцией на руках, и через неизвестное количество дней возвращается. Даёт квитанцию, оператор вносит номер квитанции и от этого отталкиваются денежные расчёты... Пропуски мешают "контролирующему органу", а ему про триггеры объяснять... ну в общем такая ситуация.
← →
Danilka (2003-05-27 11:04) [23]xmrz (27.05.03 10:59)
тогда, вероятно, только вот этот вариант:
Zacho © (27.05.03 10:59)
стартовать транзакцию, вставлять данные и завершать транзакцию в одной процедуре, перед печатью.
← →
xmrz (2003-05-27 11:13) [24]К сожалению блокировать таблицы нельзя, так как перед нажатием "Ok" при заполнении формы в Edit"ах и ComboBox"ах на onExit повешаны небольшие запросы, помогающие оператору заполнить форму и контролирующие некоторые параметры по бизнес-логике (для отлова попыток мошенничества) эти запросы тоже выполняются из ХП и возращают по 1..6 значений, но читают они из тех же таблиц. А если заблокировать таблицы на чтение, то работа грозит превратиться действительно в "однопользовательскую"?
← →
Zacho (2003-05-27 11:21) [25]
> xmrz (27.05.03 11:13)
Например, такой алгоритм.
После заполнения формы стартуется отдельная транзакция, в которой и происходит блокировка, получения номера и вставка данных. Если транзакция будет достаточно короткой, а операторов - не много, то может быть замедление работы будет практически незаметно.
← →
Danilka (2003-05-27 11:22) [26]xmrz (27.05.03 11:13)
блокировка, max+1, запись и завершение транзакции - в одной процедуре, доли секунд, юзера и не заметят ничего.
← →
Sergey13 (2003-05-27 11:44) [27]2xmrz (27.05.03 11:13)
Если уж так важен номер без пропусков, то раздели в базе (и главное в голове) понятия ID и порядковый номер. Первое заполняется генератором и пропуски там пофигу. Второе можно обработать в тригере после вставки - проапдейтить поле номера как max(nomer)+1 с анализом того что такой номер уже может быть создан другим юзером. Для гарантии на номер тоже можно (нужно) повесить уникальность, особливо с признаком года, например, для нумерации по году.
Страницы: 1 вся ветка
Форум: "Базы";
Текущий архив: 2003.06.19;
Скачать: [xml.tar.bz2];
Память: 0.52 MB
Время: 0.008 c