[ vbvlada @ 16.02.2011. 20:54 ] @
Situacija je ovakva:
Imam denormalizovanu tabelu koja pored ID-a ima i Naziv. Postavio sam trigere AFTER UPDATE i AFTER INSERT za ažuriranje kolone Naziv.
Na koji način da sprečim update kolone Naziv?

Vidim da BEFORE UPDATE ne postoji, a INSTEAD OF mi sprečava ova dva gore navedena trigera, a da ga stavim u AFTER UPDATE, kasno je :)

Ne bih da rešenje odskoči od početnog jer je u pitanju zadatak...

Ima li neko ideju?
[ Zidar @ 16.02.2011. 22:49 ] @
Citat:
Postavio sam trigere AFTER UPDATE i AFTER INSERT za ažuriranje kolone Naziv.
Na koji način da sprečim update kolone Naziv?


Postavi strukturu tabele (CREATE TABLE, nekoliko test redova i skriptu za trigere, pa da vidimo o cemu se radi. Ovako ti ne mozemo pomoci.
[ sparc @ 17.02.2011. 07:09 ] @
postavi triggere FOR INSERT i FOR UPDATE, samo ne razumem sto ce ti
trigger za insert kada tu prvi put dodeljujes naziv, jedino mozes da
proveravas da li je naziv null ili prazan string a to mozes i constraintom.

[ vbvlada @ 17.02.2011. 09:37 ] @
Evo recimo imamo sledeći slučaj:
Tabela Proizvod (IDProizvod, Naziv)
Tabela Porudzbina (IDPor, Datum)
Tabela StavkaPor (IDPor, Rb, IDProizvod, Kol, NazivProizvod)

Uradio sam trigere:
Tabela StavkaPor: AFTER INSERT - podesi NazivProizvod da odgovara unetom proizvodu
AFTER UPDATE (IDProizvod) - podesi NazivProizvod da odgovara izmenjenom proizvodu
Tabela Proizvod: AFTER UPDATE (Naziv) - svim stavkama koje imaju odgovarajući IDProizvod, podesi NazivProizvod

E sad, ako neko želi da promeni direktno kolonu NazivProizvod, to bi trebalo preko trigera da se zabrani (možda da se pozove RAISERROR), ili makar da se onemogući promena vraćanjem na staru vrednost, mada mi je to nekako poslednja opcija.
[ sparc @ 17.02.2011. 11:10 ] @
Kolega,
u tabeli StavkaPor ti uopste ne treba polje NazivProizvod, po pravilu I normalizacije,
a sve izvestaji ili reporti se dobijaju pomocu viewa ili stored procedura
koje ce ti za IDProizvod vratiti naziv proizvoda. ili bas ako uporno zelis
da ti se naziv proizvoda u ovoj tabeli menja za unetu ili azurirani sifru proizvoda
tada uradis sledece

CREATE TRIGGER t_StavkaPor
ON StavkaPor
FOR INSERT,UPDATE
AS
BEGIN
SET StavkaPor.NazivProizvod =
(SELECT Naziv FROM Proizvod WHERE Proizvod.IdProizvod = (SELECT IdProizvod FROM Inserted))
END

Ovako ce ti u tabeli uvek naziv proizvoda da odgovara Nazivu iz tabele proizvodi,
a tvoje resenje se uopste ne sprovidi u praksi, ali ako bas moras ti izvoli.
[ Fedya @ 17.02.2011. 11:32 ] @
@sparc:
Citat:
vbvlada: Imam denormalizovanu tabelu koja pored ID-a ima i Naziv...


Citat:
vbvlada: ...ili makar da se onemogući promena vraćanjem na staru vrednost, mada mi je to nekako poslednja opcija.


Covek zna sta radi, ocigledno zna da tabela nije normalizovana i to radi sa razlogom (pretpostavljam optimizacija citanja ili cuvanja imena/cene proizvoda koje u vreme prodaje). Takodje pri tome ne zeli da resetuje vrednost bez prijave greske.

@OP
Resenje koje meni pada na pamet je da ides sa after kad vec ne mozes instead, proveris da li je ime menjano (join inserted i deleted i proveris da li se ime razlikuje) resetujes vrednost upitom koji se sparc dao i uradis RAISERROR.
[ vbvlada @ 17.02.2011. 11:33 ] @
Slađane, ne radim ovo kao realan sistem već kao zadatak, pa eto baš nekako moram, šta ću.
I zadatak je da takva kolona postoji i da treba da postoji triger koji će da spreči direktnu izmenu tog podatka.
Mislio sam da će nešto moći da se uradi baš kao što se traži, ali ok, bolje da napravim da ikako radi nego da ostavim nedorečeno.
Hvala na odgovoru!
Naravno ne mora diskusija da se završi na ovome.

Znam da postoji pravilo normalne forme, ali ovde je urađena DENORMALIZACIJA u cilju poboljšanja performansi. Ako se često koristi upit koji treba da vraća spisak porudžbine, stalno mora da se poziva JOIN. Zato je namerno narušena normalna forma, a integritet podataka se čuva korišćenjem trigera.
[ vbvlada @ 17.02.2011. 11:44 ] @
Citat:
Fedya: @sparc:
Resenje koje meni pada na pamet je da ides sa after kad vec ne mozes instead, proveris da li je ime menjano (join inserted i deleted i proveris da li se ime razlikuje) resetujes vrednost upitom koji se sparc dao i uradis RAISERROR.


Ok, ovo sam držao kao rešenje ukoliko ne postoji ništa, kako da kažem, da nije toliko peške, ali dobro, ipak mislim da će i to proći.

Inače vidim da je Oracle dosta moćniji, on ima triger koji radi sa pojedinačnim redovima, ima BEFORE i AFTER i dosta tako finih sitnica.
[ Zidar @ 17.02.2011. 16:17 ] @
Moze li ovako:
Kad insertujes stavke, ti u stvari prepisujes ime proizvoda iz tabele proizvodi - u tome je denormalizacija. OK. Razumeo sam da imas triger koji to radi, i to je OK.
Sada zelis dve stvari:

1. ako neko pokusa da promeni NazivProizvoda u tabeli Stavke da se to spreci
2. AKo se u tabeli Proizvodi promeni naziv za neki proizvod, da se ta promena propagira u atbelu Stavke.

Moze trigerima a i ne mora. Ako ti je zadatak da pokazes kako znas da napises ovakav triger onda evo:


-- 1. ako neko pokusa da promeni NazivProizvoda u tabeli Stavke da se to spreci
CREATE TRIGGER t_UPD_StavkaPor
ON StavkaPor
FOR UPDATE
AS
BEGIN
IF UPDATE(NazivProizvod) BEGIN rollback END
END

Uoci da je triger FOR UPDATE, nije FOR INSERT, UPDATE. Broj redova koji se pokusavaju promeniti nema veze, jedan ili vise, svejedno. Triger na INSERT prepisuje naziv iz tabele Proizvodi, Triger an UPDATE provereva da li je slucajno pokusan UPDATE na koloni NAzivproizvoda tabela StavkaPor, i ako jeste, odbacuje transakciju.

Onaj tvoj prvi triger, koji prepisuje ime MORA da bude samo FOR INSERT.

Zahtev 2. je u suprotnosti sa zahtevom 1. Mozes naravno na tabeli Proizvod da napises triger koji ce propagira izmenu, ali ce je triger t_UPD_StavkaPor odbaciti. Zahtev 1 kaze 'promene naziva u Stavkama se zabranjuje' a zahtev 2 kaze 'e bas hocu da proemnim nazive u Stavke kad se promeni naziv u Proizvodi'. Trigeri ce jedan drugog da ponistavaju, Znaci, trigerima ovo nece moci.

Resenje bez trigera je da koristis FK, na malo prosiren nacin, sa CASCADE UPDATE opcjom.

Tabela Proizvod (IDProizvod, Naziv, PRIMARY KEY (IDProizvod), UNIQUE (IDProizvod, Naziv) )

Tabela Porudzbina (IDPor, Datum)

Tabela StavkaPor (IDPor, Rb, IDProizvod, Kol, NazivProizvod
, FOREIGN KEY( IDProizvod, NazivProizvod) REFERENCES Proizvod (IDProizvod, Naziv) ON UPDATE CASCADE
, FOREIGN KEY (IDPor) REFERNCES Porudzbina (IDPor))
)

Trik je dodati constraint UNIQUE (IDProizvod, Naziv) na tabelu Proizvod. Naoko nepotrebno, jer je IDProizvod vec jedinstven, to je PK. Nista ne smeta da PK prositimo i dobijemo 'super key'. Zasto? Da bi mogli da na tabeli StavkaPor napravimo FOREIGN KEY. FK mora da gleda u nesto sto je UNIQUE u referenciranoj tabeli, a to je nas 'super key'. FK ce spreciti izmenu naziva u StavkaPor, svaki naziv mora da se slaze sa svojim prizvodID u svakom momentu. Time smo zadovoljili zahtev 1.

On UPDATE CASCADE na definiciji FK zavrsava posao za zahtev 2. Ako u tabeli Proizvod promenis naziv nekom proizvodu, ta ce se promena propagirati u tabelu StavkaPor, automatski. Ne treba ti triger. I to je potpuno u skaldu za Zidarevom teoremom, koja glasi "The best code is no code at all". Nazalost, ta teorem se ne uci u skolama, steta.

Primedba:
Zahtev 2 (propagacija promena imena iz Proizvod u Stavke), ima za posledicu da ako je nesto prodato kao ( IDproizvod = 1, NazivProizvod = 'kosulja muska') pre godinu dana, a mi danas promenimo u tabeli Proizvod da kaze ( IDproizvod = 1, NazivProizvod = 'kosulja za muskarce') to ce se promniti i u tabeli StavkaPor. Ako smo pre godinu dana odstampali fakturu, na njoj je pisalo 'kosulja muska'. Ako danas odstampamo tu istu fakturu, pisace 'kosulja za muskarce'. Ne znam koliko je to pametno, ali ako je takav zadatak, onda neka. IZ ovoga vidis da denormalizacija nije samo formalna stvar, vec stvara gomilu drugih problema, koje na prvi pogled i ne vidimo, jer smo fokusirani na uspostavljanje integriteta podataka na silu, trigerima i super kljucevima.

Zato je bolje zahtev 2 jednostavno izbaciti iz igre. Ako vec prenosis naziv u Stavke, OK, imas triger, lepo. Sada imas nacin i da sprecis promene - onaj drugi triger, ili FK, ali bez CASCADE UPDATE.

Nadam se da je pomoglo?







[ nadavesela @ 18.02.2011. 14:43 ] @
Dali je moguce postavljanjem constrainta CK_StavkaPorNaziv prema koloni NazivProizvod u Tabeli StavkaPor
koristeci UDF

CREATE FUNCTION [dbo].[fn_NazivProizvod] (@IDProizvodStavkaPor as int)
RETURNS NVARCHAR(100)
AS
BEGIN
DECLARE @NazivProizvod as NVARCHAR(100)
SET @NazivProizvod =(SELECT Naziv FROM Proizvod WHERE [email protected] ) RETURN @NazivProizvod
END

ALTER TABLE StavkaPor WITH NOCHECK ADD CONSTRAINT [CK_StavkaPorNaziv] CHECK NOT FOR REPLICATION (([dbo]. [fn_NazivProizvod] ([IDProizvod])=[ NazivProizvod])))
[ Zidar @ 18.02.2011. 17:07 ] @
Citat:
Dali je moguce postavljanjem constrainta CK_StavkaPorNaziv prema koloni NazivProizvod u Tabeli StavkaPor
koristeci UDF..


Moguce je ali nije potrebno, jer obican FOREIGN KEY to radi mnogo brze i pouzdanije. CHECK CONSTRAINT treba bazirati na UDF samo kada nema resenja. Otprilike ovaoko ide redosled 'valjanosti' postavljanja ogranicnja. ides ovim redom, pa ako ne moze jedno, ides na sledece.

1) FOREIGN KEY
2) CHECK bez UDF
3) CHECK sa UDF ili triggers
4) stored procedures
5) application code

CHECK with UDF deluje jako privlacno u nekim slucajevima, ali treba biti oprezan. Kod multi-row UPDATE moguce je preveriti CHECK. Imas primer u knjizi "Defenesive Database Programming with SQL Server", Alex Kuznetsov. Mislim da se moze skinuti jos uvek na SQL sajtovima SQLCentral.com ili SImpalTalk.com. Googlaj Alex Kuznetsov i naci ces sigurno.


[ vbvlada @ 21.02.2011. 09:36 ] @
Zdravo, izvinjavam se što se nisam javljao, bio sam u gužvi.

Zadatak mi je bio baš sa trigerima da napravim tu funkcionalnost, dakle morao sam da pokažem da znam to da uradim.
Na kraju nisam zabranio da se direktno dira NazivProizvoda i naravno dobio koji poen manje.

Ona dva trigera koja si naveo jesu u suprotnosti, i bez privremenog isključivanja onog "strožijeg" to ne može da se uradi, baš kao što si rekao.

Šta tačno znači FOR? Znam za AFTER i INSTEAD OF...
[ Fedya @ 21.02.2011. 09:46 ] @
FOR je isto sto i AFTER
[ sparc @ 21.02.2011. 10:30 ] @
FOR se okida pre sih desavanja nad bazom pre constrainta, FK i cascada,
znaci ne dasva se nista nad bazom dok ne prodje FOR triger,
koristi se za custom validacije, a
AFTER se desava kada se svi constrainti dese, cascade i zapis u bazu,
koristi se da bi se trigerski uredila neka druga tabela u bazi,
mozes i da razdvojis ova dva triggera
toliko iz iskustva
[ Fedya @ 21.02.2011. 11:14 ] @
INSTEAD OF se okida pre akcije, AFTER i FOR posle akcije.

FOR i AFTER su isto, samo je jedno ANSI naziv drugo MS.
[ vbvlada @ 21.02.2011. 12:11 ] @
Malo je bezveze što ne postoji BEFORE, jer bi u ovom slučaju bilo mnogo lakše da zabranim UPDATE.
INSTEAD OF zamenjuje update, pa kada bih tako radio onda bih morao ručno da radim ažuriranje...
[ nadavesela @ 21.02.2011. 12:22 ] @
Posto 'smo' lepo resili skolski zadatak trigerima, preskocivsi neka pravila, napomenula bih samo ovo, da sam mislila da ogranicenje promene naziva u tabeli StavkaPor i potreba za denormalizacijom (da nema Join-a), je sa ciljem da se obezbedi neka vrsta trajnosti podataka, odnosno da ako je u momentu kreiranja fakture naziv proizvod bio jedan, isti treba ostati i ako se faktura stampa nakon nekog vremena, a proizvod je u medjuvremenu promenio naziv (cenu...).

U slucaju da je obezbedjena situacija nemogucnosti izmene kolone Naziv u tabeli StavkaPor, moguca je sledeca situacija; da je naziv u tabeli Proizvod unesen pogresno (tehnicka greska), da je isti unet u tabelu StavkaPor, da je nakon toga sagledana tehnicka greska, i da sad se javlja potreba za izmenom Naziva u svim redovima tabele StavkaPor gde je pogresno unesen.

Znaci pravilo broj jedan: koja je potreba i cilj, a onda bih trajnost podataka uradila 'igrajuci' se identifikatorima reda (vestackim kljucevima) konsultacionih tabela, kako bi se obezbedili svi relevantni podaci koji su bili validni u datom trenutku . To bi znacilo da se svaka bitna promena nad konsultacionom tabelom evidentira kao insert, da se zna koji red iz kojeg proizilazi, da se zna koji su aktivni redovi, da samo za tehnicku izmenu je moguc update, da nije moguce brisanje redova koji su neaktivni (odnosno koji su promenjeni), da su promenjeni redovi dostupni samo za read only.
[ Zidar @ 21.02.2011. 15:51 ] @
@ nadavesle: svaka cast za rezonovanje. Ovako kako si navela i treba da se radi:
Citat:
To bi znacilo da se svaka bitna promena nad konsultacionom tabelom evidentira kao insert, da se zna koji red iz kojeg proizilazi, da se zna koji su aktivni redovi, da samo za tehnicku izmenu je moguc update, da nije moguce brisanje redova koji su neaktivni (odnosno koji su promenjeni), da su promenjeni redovi dostupni samo za read only.

Problem je sto to nije lako napraviti. Nije lako, jer nas to ne uce u skoli. Medjutim, pametan svet svasta smisli, pa tako i ovo. Ako umes ovo da uradis, bice nam drago da cujemo kako. Ako ne umes, da li te zanima, ja znam ko ume pa mogu da prepisem resenje
[ nadavesela @ 25.02.2011. 10:16 ] @
Zidar, trudim se da MS SQL relacionom bazom , primenim nacin rezonovanja. Na fakultetu ne uce svemu, al je dobro ako ti pruze slobodu da rezonujes, iako je to nesto sto praksa omogucava.
I pored toga koliko umem drago mi je cuti bilo kakvu ideju ili sugestiju u istom pravcu, iako je to vec deo neke teme iz foruma Baze Podataka. Cak bi htela cuti neka iskustva sa objektnim bazama.