MSSQL update trigger проблем

12 отговора 991 прегледа
#1

MSSQL update trigger проблем

Знам за този проблем от преди, но чак сега ми се налага да не го заобикалям, а да го реша.
Опитвам се при update на конкретни полета в една таблица, да променя и съответстващи им полета в друга. Кодът в тригера е нещо такова:

UPDATE MyTable
SET
[field1] = (SELECT [field1] FROM inserted),
[feild2] = (SELECT [field2] FROM inserted)
FROM MyTable
JOIN deleted ON
MyTable.[field3] = deleted.[field3] AND
MyTable.[field1] = deleted.[field1] AND
MyTable.[field2] = deleted.[field2]

и това се изпълнява, ако имам само 1 променен ред в inserted (правя проверка).
Е, да, ама се случва да имам и повече редове в inserted. Та това ме води и до конкретния проблем: как да приложа правилните промени в правилните редове на MyTable. Как да разбера кой ред в inserted на кой ред в deleted съответства?
Трябва например да имам нещо такова:
[field1] = (SELECT [field1] FROM inserted WHERE ......?????) --или JOIN...?

Дали си зададох ясно въпроса?:)

Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
Отговор
в отговор на devnul #2

Re: MSSQL update trigger проблем

Аз поне се сещам за 2 варианта - единия е да си направиш курсор по inserted или deleted и тогава за всеки ред ще си правиш каквото си правил досега.
Другия (който е по-производителен) е да използваш update from
нещо от вида на:
update myTable
set field1=i.field1
from
myTable inner join inserted as i on myTable.MyID=i.MyID

като цяло не е препоръчително да се правят update от типа
set field=(select ... from ...) винаги, когато е възможно използвай join-ове. От гледна точка на производителността е по-добре.


P.S. Дано съм разбрал правилно въпроса.

Отговор
в отговор на Blandings Castle #3

Re: MSSQL update trigger проблем

Да, прав си за join-овете, просто в случая ставаше въпрос за допускане само на 1 ред в inserted, а тогава няма проблем с производителността.
Но сега осъзнавам, че пропуснах да спомена особено важен аспект от проблема. Ти си прав за join-a, с курсори не ми се е налагало да работя, но разбирам какво имаш предвид. Но точно в този конкретен случай аз все още не виждам как ще стане така. Защото променените полета са от primary key - a.

Ще се опитам да опиша 1 такава проблемна постановка.
2 таблици, схемите са:
A(pk1, pk2, field1, field2...) - pk1 и pk2 са ключ
B(pk1, pk2, pk3, f1, f2, f3...) - pk1, pk2 и pk3 са ключ
Искаме тригер от A към B:
Ако сменим стойността на pk1 в A, то тази нова стойност трябва да се запише и в полето pk1 на B, на мястото на съответстващата стара. Нещо като Cascade Update за Foreign key constraint. Същото и за полето pk2.

Сега да вземем конкретни възможни кортежи:
В таблицата A:
1. (1, 1, 'blabla', 'blabla2', ...)
2. (2, 1, 'blabla', 'blabla2', ...)
3. (1, 2, 'blabla', 'blabla2', ...)
4. (2, 2, 'blabla', 'blabla2', ...)

В таблица B:
1. (1, 1, 1, 'blabla', 'blabla2', ...)
2. (2, 1, 2, 'blabla', 'blabla2', ...)
3. (1, 2, 3, 'blabla', 'blabla2', ...)
4. (2, 2, 4, 'blabla', 'blabla2', ...)

И например имаме следния update:
UPDATE A
SET pk2 = pk2 + 1

Сега искаме промените да отидат на съответното място и в B.
Join на B с inserted? По кои полета? pk1 не ни гарантира връзка с единствен запис от inserted, а pk2 вече имат различни стойности в inserted и B. Може би трябва и е възможно да минем и през deleted, там имаме съответствието на старите редове. Е, да де, стигнах и до най-същественото изречение от първоначалния си пост:
Как да разбера кой ред в inserted на кой ред в deleted съответства?
Аз тук изкуствено си сложих номерация горе. А има ли нещо системно в MSSQL, което да дава съответствието: при update-a на таблицата, този ред (в deleted) беше изтрит и на негово място сега е ето този ред (от inserted)

Забележка: Моят проблем вече е решен, пак заобиколно. Тази дискусия, обаче, все още ме интересува! Заради спорта, пък и за да знам занапред. То и аз ще си търся информация, но ако някой знае нещо, което да хвърли светлина върху проблема... моля, споделете!

P.S. Уф, че било трудно да задаваш въпроси в писмен вид. Простете ми, ако още не го владея този номер! Аз обикновено драскам скици и жестикулирам - много е информативно!

Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
Отговор
в отговор на devnul #4

Re: MSSQL update trigger проблем

Първо да спомена че сам си си направил живота труден - съставния PK никога не ми е изглеждал добро решение. Принципно си е въпрос на DBDesign да си направиш една ID колона на таблицата(която да ти е уникална - Identity в MSSQL, със SEQUENCE в ORACLE), по която да се join-ваш после на воля. Ако го имаше това, часто от въпросите ти отпадат от само себе си (имаш точна локация, кой ред кой е в таблицата, в inserted и в deleted.)
Горещо ти препоръчвам ако имаш възможност да си сложиш такива PK - една identity колона ти спестява много главоболия и сложни join-ове и върши работата на номерация на редовете - може да е с дупки (при rollback), но е стабилен идентификатор. А ако искаш да нямаш повторения по съставния ключ, по добре за него си направи unique index.

Отговор
в отговор на Blandings Castle #5

Re: MSSQL update trigger проблем

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

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

Съставните ключове са направени, следвайки логиката на информацията по таблиците. По същата логика са направени връзките. Както и всичко друго. На мен базата ми изглежда добре проектирана (не съм я правила аз). Съставният ключ ми гарантира уникалност на комбинацията от полета - точно такива са ми правилата за реалната информация, която ще се събира. Освен това са ми необходими сумати Foreign key constraint-и по няколко полета. Имам спомен, че MSSQL се оплаква, когато участващите полета в Primary key table- а не образуват ключ. Ако сложа навсякъде отделно поле pk identity, реално се отказвам от foreing keys и ще има да си пиша тригери (вече и по двете таблици! защото трябва да правя и проверка дали не се опитвам във "foreing key" таблицата да слагам стойности, които ги няма в "primary key" таблицата). Още повече, че съвсем наскоро си имахме главоблъскания с една identity колона, но това е друга история.

Е, сега като изложих тази ситуация, да взема и да започна с въпросите. Нямам много опит, все още, с бази данни. Още по-малко с DBDesign. Но това ми е интересно и искам да науча повече. Ако имаш интересни линкове към полезна информация по повод на основни правила при проектиране на бази данни, моля те, прати ми ги (някои теоретични неща съм ги учила - нормални форми и т.н.). Предполагам, обаче, най-добрите правила се научават с опита.
Твоят вариант - с identity колони за primary key - виждаш ли приложение в изложения от мен случай? Доколко дизайна на базата трябва да се прави предвид логиката на информацията? И доколко съобразно логиката на лесното й ползване при писане на приложенията върху нея?

С риск да стана нахална... преди време мярнах коментар в един постинг, за това, че връзка и външен ключ са различно нещо, както и че този въпрос бил достатъчно разискван. Някакъв шанс да ми кажете какво точно имате предвид? (явно не знам какво се разбира под връзка)
Е, простете ми невежеството, надявам се лека - полека да изчистя този проблем. Надявам се междувременно да не досаждам прекалено много...

Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
Отговор
в отговор на devnul #6

Re: MSSQL update trigger проблем

Първо малко линкове-
[линк]
Има доста неща за бази данни - MSSQL, Oracle, DB2, MySQL..

[линк]

като казваш история на структурата, да не би идеологията да е наследена от clipper-ско приложение - там се използват мощно съставни ключове.

Уникалност на комбинация от полета ти гарантира и Unique index
"CREATE UNIQUE INDEX index_name ON Table_Name.....".
Аз лично бих предпочел PrimaryKey, който е Identity, a за съставния ключ уникален индекс. Почти сигурно е, че този вариант ще работи и в твоя случай, проблема е по-скоро в следващите нива на приложението - най-вероятно логиката на програмата е обвързана с настоящата структура на базата.
Темата за дизайна е наистина дълбока за да я дискутираме във форум, но погледни за пример базите NorthWind и Pubs които си вървят с дистрибутива на MSSQL(даже май и с Access-a).

Отговор
в отговор на Blandings Castle #7

Re: MSSQL update trigger проблем

За clipper - не:)
А това с unique index-a звучи логично...
Благодаря много за отделеното внимание!:)
Ще взема да се пообразовам малко:)
Лека работа!

Bad command or file name. Bad, bad command! Sit! Stay! Staaaay...
Отговор
в отговор на devnul #8

Re: MSSQL update trigger проблем

Та значи и аз се сблъсках със следния проблем - как да намеря съответстващите записи в inserted и updated таблиците при UPDATE TRIGGER. И направих следното:
В тригера използвах два курсора - за inserted и updated таблицата. И двата ги обхождам паралелно в един цикъл. Интересно, че при всяко обхождане и FETCH-ване на данните излизат съответстващите записи.

DECLARE delete_cursor CURSOR LOCAL FAST_FORWARD FOR SELECT blablabla FROM DELETED
DECLARE insert_cursor CURSOR LOCAL FAST_FORWARD FOR SELECT blablabla2
FROM INSERTED

OPEN delete_cursor
OPEN insert_cursor
FETCH NEXT FROM delete_cursor INTO
@blablabla
FETCH NEXT FROM insert_cursor INTO
@blablabla2
WHILE @@FETCH_STATUS = 0 BEGIN
--- do something
END

FETCH NEXT FROM delete_cursor INTO
@blablabla
FETCH NEXT FROM insert_cursor INTO
@blablabla2 END
CLOSE delete_cursor
CLOSE insert_cursor
DEALLOCATE delete_cursor
DEALLOCATE insert_cursor
END

Потърсих MSDN-а, потърсих и Google-то и никъде не пишеше, че двете таблици са задължително симетрични. Интересно случайно ли се получава да излизат съответстващите записи при всяко FETCH-ване на двете таблици

Отговор
в отговор на Wolfheart #9

Re: MSSQL update trigger проблем

Страничен ефект. Би следвало да fail-не грозно при паралелен exеcution план (на двупроцесорна машина или в yukon?). При всички случаи си е мега хак.

cheers,
</wqw>

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

Re: MSSQL update trigger проблем

PK constraint = UNIQUE constraint върху NOT NULL-able колона (това е тъждествено равно:-)). В твоя случай един синтетичен ключ

ALTER TABLE MyTable ADD

MyID INT IDENTITY(1, 1) -- identity колони са NOT NULL по принцип
, CONSTRAINT UQ_MyID UNIQUE (MyID)


... би трябвало да ти реши проблема. Терминът Primary key е супер измислен. Принципно keys (PK и UNIQUE) могат да бъдат target на FK без проблем. Аз лично предпочитам синтетични (surrogate) PK за вдигане на релации между таблиците, но упорствам всяко entity да има natural key (просто composite UNIQUE), които да може да бъде сверяван от клиента с real-life артефакти (документи, ЕГН, etc.)

Основно провило при избиране на "primary" key -- immutable -- каскадните update/delete са кърпежи на растежа :-))

cheers,
</wqw>

Отговор
в отговор на Wolfheart #11

Re: MSSQL update trigger проблем

Никак не е случайно даже.
<sql server books online>
An update transaction is similar to a delete operation followed by an insert operation; the old rows are copied to the deleted table first, and then the new rows are copied to the trigger table and to the inserted table.
</sql server books online>

[линк]

Отговор
в отговор на Blandings Castle #12

Re: MSSQL update trigger проблем

Но не пише нищо за последователността, Нали се сещаш, че едино от определенията за релационни бази данни беше, че последователността на записити в таблицата няма значение. Та на мене ми се е случвало в някоя таблица да си инсъртна някой запис и след нормален SELECT без ORDER BY клауза да ми изкара току-що вкарания запис в средата на резълт-сета.

Но това нали не са таблици, а нормални масивчета, които са представени, като таблици, може да си пази последователността при записите.

Отговор
в отговор на Wolfheart #13

Re: MSSQL update trigger проблем

Масивчета? Хмм, по-скоро мисли за тези виртуални таблици (BOL definition) като за view-та върху таблицата.

За малко да забравя: последовятелността на FETCH на записите от курсор зависи от ORDER BY клаузата на source SELECT-а и е неопределен ако такава липсва.

cheers,
</wqw>

Отговор