sql trigger & transaction
13 отговора
1000 прегледа
sql trigger & transaction
В момента работя с MySQL, но въпроса ми е принципен. Предпочитам да не използвам специфични особености на отделен SQL сървър.
По редица причини трябва аз да генерирам ключа на таблицата.
1. Дали тригера, който е най-долу ще работи както трябва. Т.е. дали има някаква атомарност и ще се изпълни все едно има изолация SERIALIZABLE?
2. Ако не -- може ли по някакъв друг начин да стане с тригер?
3. Ако и това не -- предполагам, че ще трябва да за браня INSERT в таблицата и да създам отделна процедура, която да INSERT-ва.
CREATE TRIGGER `ins_document` BEFORE INSERT ON `document` FOR EACH ROW
BEGIN
SELECT Max(Id) INTO @AId FROM document;
IF IsNull(@AId) THEN
SET @AId=1;
ELSE
SET @AId=@AId+1;
END IF;
SET NEW.Id = @AId;
END
По редица причини трябва аз да генерирам ключа на таблицата.
1. Дали тригера, който е най-долу ще работи както трябва. Т.е. дали има някаква атомарност и ще се изпълни все едно има изолация SERIALIZABLE?
2. Ако не -- може ли по някакъв друг начин да стане с тригер?
3. Ако и това не -- предполагам, че ще трябва да за браня INSERT в таблицата и да създам отделна процедура, която да INSERT-ва.
CREATE TRIGGER `ins_document` BEFORE INSERT ON `document` FOR EACH ROW
BEGIN
SELECT Max(Id) INTO @AId FROM document;
IF IsNull(@AId) THEN
SET @AId=1;
ELSE
SET @AId=@AId+1;
END IF;
SET NEW.Id = @AId;
END
Re: sql trigger & transaction
По принцип ще работи. Има обаче тънкости.
Ако това ще е номер на документ и си позволиш лукса да разрешиш изтриване на документи можеш да достигнеш до ситуация, в която си разпечатил документ примерно 123, след това изтриваш този документ и вкарваш следващ пореден, нямащ нищо общо с предходния. И отново правиш разпечатка. Тогава в базата данни всичко е ОК, обаче на хартия имаш 2 документа с един и същи номер и със съвсем разлино съдържание.
Абе в общи линии ако в тази таблица ще е разрешено изтриване аз бих търсил друг вариант.

Ако това ще е номер на документ и си позволиш лукса да разрешиш изтриване на документи можеш да достигнеш до ситуация, в която си разпечатил документ примерно 123, след това изтриваш този документ и вкарваш следващ пореден, нямащ нищо общо с предходния. И отново правиш разпечатка. Тогава в базата данни всичко е ОК, обаче на хартия имаш 2 документа с един и същи номер и със съвсем разлино съдържание.
Абе в общи линии ако в тази таблица ще е разрешено изтриване аз бих търсил друг вариант.
Re: sql trigger & transaction
"Предпочитам да не използвам специфични особености на отделен SQL сървър. "
Ами то синтаксисът на Тригерите е спицифичен за всеки отделен сървър ...
Да не говорим за :
SELECT ... INTO @var
"дали има някаква атомарност и ще се изпълни все едно има изолация SERIALIZABLE?"
Съжалавям ама нещо бъркаш понятията.
Атомарността и изолацията не са свързани. Нито едно от двете не ти гарантира другото.
Ако опасението ти е, че преди тригерът да завърше работата си някой друг може да успее да добави същата стойност и да получиш "Duplicate key" грешка то това няма опасност да стане. При MySQL дори не става въпрос за изолация на транзакциите. Ако си с MyISAM INSERT просто заключва цялата таблица докато приключи.
По-интересно е какво би се случило при вмъкване на много редове едновременно с:
INSERT ... VALUES(...), (...), (...) ...;
ако това става в транзакция.
В такъв случай на тоерия има шанс две транзакции да се опитат да вмъкнат еднакви редове следоватлно ще трябва да използваш само ниво SERIALIZABLE ако искаш да избегнеш възможността една или повече транзакции да бъдат отказани в такава ситуация.
"Ако не -- може ли по някакъв друг начин да стане с тригер?"
Това което правиш не е ли емулация на AUTO_INCREMENT с добавката, че твоят вариант ще използва повторно последните стойности ако има изтрити редове на края на последователността, което принципно не е хич добра идея?
salle.online.bg/sig
Ами то синтаксисът на Тригерите е спицифичен за всеки отделен сървър ...
Да не говорим за :
SELECT ... INTO @var
"дали има някаква атомарност и ще се изпълни все едно има изолация SERIALIZABLE?"
Съжалавям ама нещо бъркаш понятията.
Атомарността и изолацията не са свързани. Нито едно от двете не ти гарантира другото.
Ако опасението ти е, че преди тригерът да завърше работата си някой друг може да успее да добави същата стойност и да получиш "Duplicate key" грешка то това няма опасност да стане. При MySQL дори не става въпрос за изолация на транзакциите. Ако си с MyISAM INSERT просто заключва цялата таблица докато приключи.
По-интересно е какво би се случило при вмъкване на много редове едновременно с:
INSERT ... VALUES(...), (...), (...) ...;
ако това става в транзакция.
В такъв случай на тоерия има шанс две транзакции да се опитат да вмъкнат еднакви редове следоватлно ще трябва да използваш само ниво SERIALIZABLE ако искаш да избегнеш възможността една или повече транзакции да бъдат отказани в такава ситуация.
"Ако не -- може ли по някакъв друг начин да стане с тригер?"
Това което правиш не е ли емулация на AUTO_INCREMENT с добавката, че твоят вариант ще използва повторно последните стойности ако има изтрити редове на края на последователността, което принципно не е хич добра идея?
salle.online.bg/sig
Благодаря + още 1 въпрос
Благодаря за помощта.
За VladoR : За това става въпрос. След разпечатване няма да има триене, нито промяна, иначе може да станат тези проблеми, какъвто и номер да генерираш.
За salle : Е, ще ползвам процедура с изолация SERIALIZABLE, което винаги е сигурно.
Възникна и друг въпрос: номерата на connection Id, както се дават от SHOW FULL PROCESSLIST, винаги ли нарастват, докато се не рестартира сървъра.
За VladoR : За това става въпрос. След разпечатване няма да има триене, нито промяна, иначе може да станат тези проблеми, какъвто и номер да генерираш.
За salle : Е, ще ползвам процедура с изолация SERIALIZABLE, което винаги е сигурно.
Възникна и друг въпрос: номерата на connection Id, както се дават от SHOW FULL PROCESSLIST, винаги ли нарастват, докато се не рестартира сървъра.
Re: Благодаря + още 1 въпрос
За salle : Е, ще ползвам процедура с изолация SERIALIZABLE, което винаги е сигурно.
И което със СИГУРНОСТ не е пренсимо между различни СУБД
Защо все пак не използваш auto_increment?
"номерата на connection Id"
SELECT CONNECTION_ID(); може би ще ти подскаже :)
Естествено, че нарастват докато не се превърти брояча. А той доколкото помня е ulong т.е. зависи от платформата
salle.online.bg/sig
И което със СИГУРНОСТ не е пренсимо между различни СУБД
Защо все пак не използваш auto_increment?
"номерата на connection Id"
SELECT CONNECTION_ID(); може би ще ти подскаже :)
Естествено, че нарастват докато не се превърти брояча. А той доколкото помня е ulong т.е. зависи от платформата
salle.online.bg/sig
пак питам
Има ли някакъв начин show processlist да се ползва в select?
Нещо като:
select * from x
where col_1 = (show processlist)
но естествено ми трябва само колона Id от списъка с процесите.
Нещо като:
select * from x
where col_1 = (show processlist)
но естествено ми трябва само колона Id от списъка с процесите.
Re: Интересно ...
Не не може.
За пръв път чувам някой да иска подобна възможност!
За какво всъщност ти трябва?
Да не би да пазиш някъде SELECT CONNECTION_ID(); ?
Не мога да схвана идеята.
salle.online.bg/sig
За пръв път чувам някой да иска подобна възможност!
За какво всъщност ти трябва?
Да не би да пазиш някъде SELECT CONNECTION_ID(); ?
Не мога да схвана идеята.
salle.online.bg/sig
Re: Интересно ...
Благодаря за вниманието, жалко, че никой друг не се включва напоследък.
В момента имам следния проблем, който обмислям и нямам решение още:
Конкуренто редактиране на ред от таблица.
Sql сървър + клиентска програна (exe), приемам че друг няма достъп за запис в тази база.
В една колона, мисля да записвам 0 или CONNECTION_ID(). Ако има 0 значи реда е достъпен за редакция, иначе съответната сесия е "заключила" този ред. При започване на редакция, ако в тази колона има 0 записвам CONNECTION_ID(), при завършване update на реда и записвам 0. Проблема е когато клиента забие, прекъсне му мрежата или нещо подобно, и не запише 0 за да освободи реда. Трябва да се проверява дали стойноста е активен процес (connection).
Естествено, като обмислих нещата, show processlist не може да се включва в транзакции, защото няма нищо общо с таблиците, а със съвсем други събития. От друга страна, в моя случай, нещата зависят от няколко потребителски дейтвия (забива, влиза пак, заключва ред), така че спокойно мага да направя 2 заявки, които винаги ще са по-бързи от нещо, което се прави от потребителя.
Следя този форум от около половин година и имаше подобни въпроси, но не си спомням за добро решение.
В момента имам следния проблем, който обмислям и нямам решение още:
Конкуренто редактиране на ред от таблица.
Sql сървър + клиентска програна (exe), приемам че друг няма достъп за запис в тази база.
В една колона, мисля да записвам 0 или CONNECTION_ID(). Ако има 0 значи реда е достъпен за редакция, иначе съответната сесия е "заключила" този ред. При започване на редакция, ако в тази колона има 0 записвам CONNECTION_ID(), при завършване update на реда и записвам 0. Проблема е когато клиента забие, прекъсне му мрежата или нещо подобно, и не запише 0 за да освободи реда. Трябва да се проверява дали стойноста е активен процес (connection).
Естествено, като обмислих нещата, show processlist не може да се включва в транзакции, защото няма нищо общо с таблиците, а със съвсем други събития. От друга страна, в моя случай, нещата зависят от няколко потребителски дейтвия (забива, влиза пак, заключва ред), така че спокойно мага да направя 2 заявки, които винаги ще са по-бързи от нещо, което се прави от потребителя.
Следя този форум от около половин година и имаше подобни въпроси, но не си спомням за добро решение.
Re: Интересно ...
Ака разбирам правилно, това което искаш се реализира с транзакции и нищо повече не ти е нужно.
Мисля, че ще ти е полезно да прочетеш:
Введение в управление транзакциями часть первая
Введение в управление транзакциями Часть 2
Введение в управление транзакциями Часть 3
Введение в управление транзакциями Часть четвертая
Подобна тема беше обсъждана наскоро и тук
Мисля, че ще ти е полезно да прочетеш:
Введение в управление транзакциями часть первая
Введение в управление транзакциями Часть 2
Введение в управление транзакциями Часть 3
Введение в управление транзакциями Часть четвертая
Подобна тема беше обсъждана наскоро и тук
Re: Интересно ...
Не съм съгласен. Ще се опитам да обясня по-подробно:
1. Потребител1 избира данни за редактиране (Select)
2. Потребител1 гледа, умува (неопр. време), променя нещо.
3. Потребител1 натиска ОК (Update).
Възможно е обаче втори (трети и т.н.) потребител да избере същите данни за редактиране по време на момент 2.
Със средствата на SQL има 2 възможности:
а) update ... where key=...
б) update ... where key=... and col1=... col2=... . . . and colN=...
При вариант а) последният update ще се осъществи, при б) -- първият.
Аз не искам въобще да се допуска няколко души да редактират 1 данни едновременно. В идеалния свят, ако преди т.1 се вдигне някакъв флаг, а след т.3 флага се смъкне, няма пробеми. Ако обаче клиентската програма/ОС/хардуер забие или връзката прекъсне вече никой няма да може да редактира тези данни. Това е проблема, ако мога да го обясня по това време.
Има 1 много добро решение, което обаче е специфично за MySql, така че не мога да го ползвам, защото тогава ще ми трябва комерсиален лиценз.
1. Потребител1 избира данни за редактиране (Select)
2. Потребител1 гледа, умува (неопр. време), променя нещо.
3. Потребител1 натиска ОК (Update).
Възможно е обаче втори (трети и т.н.) потребител да избере същите данни за редактиране по време на момент 2.
Със средствата на SQL има 2 възможности:
а) update ... where key=...
б) update ... where key=... and col1=... col2=... . . . and colN=...
При вариант а) последният update ще се осъществи, при б) -- първият.
Аз не искам въобще да се допуска няколко души да редактират 1 данни едновременно. В идеалния свят, ако преди т.1 се вдигне някакъв флаг, а след т.3 флага се смъкне, няма пробеми. Ако обаче клиентската програма/ОС/хардуер забие или връзката прекъсне вече никой няма да може да редактира тези данни. Това е проблема, ако мога да го обясня по това време.
Има 1 много добро решение, което обаче е специфично за MySql, така че не мога да го ползвам, защото тогава ще ми трябва комерсиален лиценз.
Re: Интересно ...
-------
Ако обаче клиентската програма/ОС/хардуер забие или връзката прекъсне вече никой няма да може да редактира тези данни.
-------
Сам си отговаряш на въпроса. Грижата за управление на записите е на СЪРВЪРА.
ТРАНЗАКЦИИ - това е истината. В MySql не знам как е (salle?), но във FireBird заявката е следната: SELECT * FROM SOMETABLE WHERE ... WITH LOCK. Така записът се заключва още при четенето. Няма шанс друг да го прочете (ако ползва същата заявка разбира се). Ако SQL сървъра не поддържа това "WITH LOCK" - то тогава просто се погрижи всеки клиент преди SELECT заявката да пуска нещо от сорта: UPDATE SOMETABLE SET FLD1=FLD1 WHERE ... Така ако ползваш WAIT транзакции - всички ще чакат на опашка, ако ползваш NOWAIT транзакции - сървъра ще връща грешка.
Ако обаче клиентската програма/ОС/хардуер забие или връзката прекъсне вече никой няма да може да редактира тези данни.
-------
Сам си отговаряш на въпроса. Грижата за управление на записите е на СЪРВЪРА.
ТРАНЗАКЦИИ - това е истината. В MySql не знам как е (salle?), но във FireBird заявката е следната: SELECT * FROM SOMETABLE WHERE ... WITH LOCK. Така записът се заключва още при четенето. Няма шанс друг да го прочете (ако ползва същата заявка разбира се). Ако SQL сървъра не поддържа това "WITH LOCK" - то тогава просто се погрижи всеки клиент преди SELECT заявката да пуска нещо от сорта: UPDATE SOMETABLE SET FLD1=FLD1 WHERE ... Така ако ползваш WAIT транзакции - всички ще чакат на опашка, ако ползваш NOWAIT транзакции - сървъра ще връща грешка.
Re: Интересно ...
Аз не искам въобще да се допуска няколко души да редактират 1 данни едновременно. В идеалния свят, ако преди т.1 се вдигне някакъв флаг, а след т.3 флага се смъкне, няма пробеми. Ако обаче клиентската програма/ОС/хардуер забие или връзката прекъсне вече никой няма да може да редактира тези данни. Това е проблема, ако мога да го обясня по това време.
след известно време махаш флага с автоматичен скрипт. За по-чисто може да записваш "локнатите" редове в друга таблица и да не позволяваш редакцията на записи които са маркирани като "локнати" преди по-малко от Х минути.
Решението все пак е дървено, защото е на ниво приложение, а не на ниво СУБД. иначе - виж salle
след известно време махаш флага с автоматичен скрипт. За по-чисто може да записваш "локнатите" редове в друга таблица и да не позволяваш редакцията на записи които са маркирани като "локнати" преди по-малко от Х минути.
Решението все пак е дървено, защото е на ниво приложение, а не на ниво СУБД. иначе - виж salle
Re: Между другото ...
Ако държиш на заобиколните решения можеш да използваш една силно нестандартна възможност на MySQL
Прочети за функциите:
GET_LOCK()
RELEASE_LOCK()
IS_FREE_LOCK()
Става въпрос за наименовани ключалки които можеш да използваш за да "заключваш" каквотf си искаш стига логиката на заключването да се обработва в клиентската част.
(За програмистите това е интерфейс към най-обикновен mutex в сървъра)
salle.online.bg/sig
Прочети за функциите:
GET_LOCK()
RELEASE_LOCK()
IS_FREE_LOCK()
Става въпрос за наименовани ключалки които можеш да използваш за да "заключваш" каквотf си искаш стига логиката на заключването да се обработва в клиентската част.
(За програмистите това е интерфейс към най-обикновен mutex в сървъра)
salle.online.bg/sig