sql trigger & transaction

13 отговора 1000 прегледа
#1

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

Отговор
в отговор на Йopдaн #2

Re: sql trigger & transaction

По принцип ще работи. Има обаче тънкости.

Ако това ще е номер на документ и си позволиш лукса да разрешиш изтриване на документи можеш да достигнеш до ситуация, в която си разпечатил документ примерно 123, след това изтриваш този документ и вкарваш следващ пореден, нямащ нищо общо с предходния. И отново правиш разпечатка. Тогава в базата данни всичко е ОК, обаче на хартия имаш 2 документа с един и същи номер и със съвсем разлино съдържание.

Абе в общи линии ако в тази таблица ще е разрешено изтриване аз бих търсил друг вариант.

Отговор
в отговор на Йopдaн #3

Re: sql trigger & transaction

"Предпочитам да не използвам специфични особености на отделен SQL сървър. "

Ами то синтаксисът на Тригерите е спицифичен за всеки отделен сървър ...

Да не говорим за :
SELECT ... INTO @var

"дали има някаква атомарност и ще се изпълни все едно има изолация SERIALIZABLE?"

Съжалавям ама нещо бъркаш понятията.
Атомарността и изолацията не са свързани. Нито едно от двете не ти гарантира другото.

Ако опасението ти е, че преди тригерът да завърше работата си някой друг може да успее да добави същата стойност и да получиш "Duplicate key" грешка то това няма опасност да стане. При MySQL дори не става въпрос за изолация на транзакциите. Ако си с MyISAM INSERT просто заключва цялата таблица докато приключи.

По-интересно е какво би се случило при вмъкване на много редове едновременно с:
INSERT ... VALUES(...), (...), (...) ...;
ако това става в транзакция.

В такъв случай на тоерия има шанс две транзакции да се опитат да вмъкнат еднакви редове следоватлно ще трябва да използваш само ниво SERIALIZABLE ако искаш да избегнеш възможността една или повече транзакции да бъдат отказани в такава ситуация.


"Ако не -- може ли по някакъв друг начин да стане с тригер?"

Това което правиш не е ли емулация на AUTO_INCREMENT с добавката, че твоят вариант ще използва повторно последните стойности ако има изтрити редове на края на последователността, което принципно не е хич добра идея?

salle.online.bg/sig
Отговор
в отговор на Йopдaн #4

Благодаря + още 1 въпрос

Благодаря за помощта.

За VladoR : За това става въпрос. След разпечатване няма да има триене, нито промяна, иначе може да станат тези проблеми, какъвто и номер да генерираш.

За salle : Е, ще ползвам процедура с изолация SERIALIZABLE, което винаги е сигурно.

Възникна и друг въпрос: номерата на connection Id, както се дават от SHOW FULL PROCESSLIST, винаги ли нарастват, докато се не рестартира сървъра.

Отговор
в отговор на Йopдaн #5

Re: Благодаря + още 1 въпрос

За salle : Е, ще ползвам процедура с изолация SERIALIZABLE, което винаги е сигурно.

И което със СИГУРНОСТ не е пренсимо между различни СУБД

Защо все пак не използваш auto_increment?

"номерата на connection Id"
SELECT CONNECTION_ID(); може би ще ти подскаже :)

Естествено, че нарастват докато не се превърти брояча. А той доколкото помня е ulong т.е. зависи от платформата

salle.online.bg/sig
Отговор
в отговор на Йopдaн #6

пак питам

Има ли някакъв начин show processlist да се ползва в select?
Нещо като:
select * from x
where col_1 = (show processlist)
но естествено ми трябва само колона Id от списъка с процесите.

Отговор
в отговор на Йopдaн #7

Re: Интересно ...

Не не може.

За пръв път чувам някой да иска подобна възможност!

За какво всъщност ти трябва?
Да не би да пазиш някъде SELECT CONNECTION_ID(); ?

Не мога да схвана идеята.

salle.online.bg/sig
Отговор
в отговор на salle #8

Re: Интересно ...

Благодаря за вниманието, жалко, че никой друг не се включва напоследък.

В момента имам следния проблем, който обмислям и нямам решение още:
Конкуренто редактиране на ред от таблица.
Sql сървър + клиентска програна (exe), приемам че друг няма достъп за запис в тази база.

В една колона, мисля да записвам 0 или CONNECTION_ID(). Ако има 0 значи реда е достъпен за редакция, иначе съответната сесия е "заключила" този ред. При започване на редакция, ако в тази колона има 0 записвам CONNECTION_ID(), при завършване update на реда и записвам 0. Проблема е когато клиента забие, прекъсне му мрежата или нещо подобно, и не запише 0 за да освободи реда. Трябва да се проверява дали стойноста е активен процес (connection).

Естествено, като обмислих нещата, show processlist не може да се включва в транзакции, защото няма нищо общо с таблиците, а със съвсем други събития. От друга страна, в моя случай, нещата зависят от няколко потребителски дейтвия (забива, влиза пак, заключва ред), така че спокойно мага да направя 2 заявки, които винаги ще са по-бързи от нещо, което се прави от потребителя.

Следя този форум от около половин година и имаше подобни въпроси, но не си спомням за добро решение.

Отговор
в отговор на Йopдaн #9

Re: Интересно ...

Ака разбирам правилно, това което искаш се реализира с транзакции и нищо повече не ти е нужно.
Мисля, че ще ти е полезно да прочетеш:
Введение в управление транзакциями часть первая
Введение в управление транзакциями Часть 2
Введение в управление транзакциями Часть 3
Введение в управление транзакциями Часть четвертая

Подобна тема беше обсъждана наскоро и тук

Отговор
в отговор на NDeu #10

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, така че не мога да го ползвам, защото тогава ще ми трябва комерсиален лиценз.

Отговор
в отговор на Йopдaн #11

Re: Интересно ...

SELECT ... FOR UPDATE ...

salle.online.bg/sig
Отговор
в отговор на Йopдaн #12

Re: Интересно ...

-------
Ако обаче клиентската програма/ОС/хардуер забие или връзката прекъсне вече никой няма да може да редактира тези данни.
-------

Сам си отговаряш на въпроса. Грижата за управление на записите е на СЪРВЪРА.
ТРАНЗАКЦИИ - това е истината. В MySql не знам как е (salle?), но във FireBird заявката е следната: SELECT * FROM SOMETABLE WHERE ... WITH LOCK. Така записът се заключва още при четенето. Няма шанс друг да го прочете (ако ползва същата заявка разбира се). Ако SQL сървъра не поддържа това "WITH LOCK" - то тогава просто се погрижи всеки клиент преди SELECT заявката да пуска нещо от сорта: UPDATE SOMETABLE SET FLD1=FLD1 WHERE ... Така ако ползваш WAIT транзакции - всички ще чакат на опашка, ако ползваш NOWAIT транзакции - сървъра ще връща грешка.

Отговор
в отговор на Йopдaн #13

Re: Интересно ...

Аз не искам въобще да се допуска няколко души да редактират 1 данни едновременно. В идеалния свят, ако преди т.1 се вдигне някакъв флаг, а след т.3 флага се смъкне, няма пробеми. Ако обаче клиентската програма/ОС/хардуер забие или връзката прекъсне вече никой няма да може да редактира тези данни. Това е проблема, ако мога да го обясня по това време.

след известно време махаш флага с автоматичен скрипт. За по-чисто може да записваш "локнатите" редове в друга таблица и да не позволяваш редакцията на записи които са маркирани като "локнати" преди по-малко от Х минути.

Решението все пак е дървено, защото е на ниво приложение, а не на ниво СУБД. иначе - виж salle

Отговор
в отговор на Йopдaн #14

Re: Между другото ...

Ако държиш на заобиколните решения можеш да използваш една силно нестандартна възможност на MySQL

Прочети за функциите:
GET_LOCK()
RELEASE_LOCK()
IS_FREE_LOCK()

Става въпрос за наименовани ключалки които можеш да използваш за да "заключваш" каквотf си искаш стига логиката на заключването да се обработва в клиентската част.

(За програмистите това е интерфейс към най-обикновен mutex в сървъра)

salle.online.bg/sig
Отговор