Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.

Computed DatumDo

[es] :: MS SQL :: Computed DatumDo

Strane: 1 2 3

[ Pregleda: 6368 | Odgovora: 52 ] > FB > Twit

Postavi temu Odgovori

Autor

Pretraga teme: Traži
Markiranje Štampanje RSS

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Computed DatumDo25.03.2011. u 08:45 - pre 142 meseci
Ako imam tabelu u kojoj pratim istoriju promena nad datim pocetnim zapisom (pr. PocetnoId=1151). Pri samom upisu dovoljno mi je da upisem DatumOd, kao datum od kad vazi promena.

Id DatumOd StatusId PrethId PocetnoId computed DatumDo
1151 06/11/2009 0 ----- 1151 15/12/2010
1745 15/12/2010 0 1151 1151 16/12/2010
1759 16/12/2010 1 1745 1151 -----------

Ako bih zelela na nivou reda da imam DatumDo, dali nekako preko Computed Columns mogu dobiti DatumDo, ili bih i to trebala ciniti u momentu promene, i raditi fizicki zapis DatumDo u redu koji se updetuje, pre nego sto insertujem novi red.



[Ovu poruku je menjao nadavesela dana 25.03.2011. u 16:31 GMT+1]
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo25.03.2011. u 14:40 - pre 142 meseci
Na dobrom si putu otprilike, pomoci cemo ti da se ne zapetljas. Ovako.

Imas recimo neke entitete koji kroz vreme menjaju status. Recimo zbog jednostavnosti, da je dozvoljen prelazak iz bilo kog statusa u bilo koji status. jedino sto je bitno je da zabelezimo da se status promenio u oderdjenom trenutku za odredjeni entitet.

Intuitivno resenje bi bilo ovo:
Code:

GO
CREATE TABLE Status_History
(
ID int NOT NULL    -- ID je id nekog entiteta za koga pratimo promenu statusa
, StatusID int NOT NULL
, DatumOd DateTime NOT NULL
, PRIMARY KEY (ID, statusID, DatumOd)
)
GO

-- unesimo po nekoliko promena statuso
-- za entitete ciji su ID = 1 i ID = 2
INSERT INTO Status_History (ID, StatusID, DatumOD)
SELECT 1, 0, '20110117' UNION
SELECT 1, 1, '20110118' UNION
SELECT 1, 2, '20110119' UNION
SELECT 1, 0, '20110120' UNION
SELECT 2, 1, '20110117' UNION
SELECT 2, 0, '20110118' UNION
SELECT 2, 2, '20110119' UNION
SELECT 2, 1, '20110120' 
;

-- da vidimo sta smo dobili:
SELECT * FROm Status_History
ORDER BY Id, DatumOD
;

         ID    StatusID DatumOd
----------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000
          1           1 2011-01-18 00:00:00.000
          1           2 2011-01-19 00:00:00.000
          1           0 2011-01-20 00:00:00.000
          2           1 2011-01-17 00:00:00.000
          2           0 2011-01-18 00:00:00.000
          2           2 2011-01-19 00:00:00.000
          2           1 2011-01-20 00:00:00.000

(8 row(s) affected
/* 
entitet ciji je ID = 1 je poceo u statusu 0 pa isao u 1, pa 2 pa se opet vratio u 0
entitet ciji je ID = 2 je poceo u statusu 1 pa isao u 0, pa 2 pa se opet vratio u 1
*/

(8 row(s) affected)


Ideja je zdrava, ovako nekako se ocekuje da se prati istorija - svaka promena statusa se upise sa datumom kad se desila i to je to. Bar tako bi bilo na papiru ili u Excelu.

"Lako" je napisati kveri koji nam pokazuje DatumOd-datumdo, pa nema potrebe za calculated kolonama:
Code:

SELECT 
    H1.ID, H1.StatusID, H1.DatumOd
    , DatumDo = (
                 SELECt MAX(DatumOd) 
                                FROM Status_History AS H3
                                WHERE H3.ID = H1.ID
                                AND H3.DatumOD > H1.DatumOd
                )                            
FROM Status_History AS H1
ORDER BY H1.ID, H1.datumOD
;

         ID    StatusID DatumOd                 DatumDo
----------- ----------- ----------------------- -----------------------
          1           0 2011-01-17 00:00:00.000 2011-01-20 00:00:00.000
          1           1 2011-01-18 00:00:00.000 2011-01-20 00:00:00.000
          1           2 2011-01-19 00:00:00.000 2011-01-20 00:00:00.000
          1           0 2011-01-20 00:00:00.000 NULL
          2           1 2011-01-17 00:00:00.000 2011-01-20 00:00:00.000
          2           0 2011-01-18 00:00:00.000 2011-01-20 00:00:00.000
          2           2 2011-01-19 00:00:00.000 2011-01-20 00:00:00.000
          2           1 2011-01-20 00:00:00.000 NULL

(8 row(s) affected)


U sustini, prikazano resenje je naivno. Zasto je ovo naivno resenje? Deluje privlacno. Pamti se minimalan broj podataka, DatumDo mozemo da izracunamo, sve izgleda OK. Medjutim, nema nicega sto nam garantuje da ce se podaci uneti korektno. Sve ce lepo da radi dok mi pedantno i bez greske unosimo podatke. Ali ako pogresimo - zlo i naopako.

Uneo sam po nekoliko promena za ID=1 i ID = 2. Ko mi brani da sad upisem nesto ovako:

Code:

-- Try to insert incorrect data
INSERT INTO Status_History (ID, StatusID, DatumOD)
SELECT 1, 3, '20110117' 
-- no problem, INSERT went through => (1 row(s) affected)


Dodao sam jos jednu promenu za ID=1 na dan 17 Januar 2011. Vec imam jednu promenu za taj dan, mozda i mogu dve promene da budu u jednom danu, ali zasto bih tu promenu upisao tek sada, nekoliko nedelja kasnije? Sta ce da mi vrati upit datumOd - DatumDo:

Code:

         ID    StatusID DatumOd                 DatumDo
----------- ----------- ----------------------- -----------------------
          1           0 2011-01-17 00:00:00.000 2011-01-20 00:00:00.000
          1           3 2011-01-17 00:00:00.000 2011-01-20 00:00:00.000
          1           1 2011-01-18 00:00:00.000 2011-01-20 00:00:00.000
          1           2 2011-01-19 00:00:00.000 2011-01-20 00:00:00.000
          1           0 2011-01-20 00:00:00.000 NULL
          2           1 2011-01-17 00:00:00.000 2011-01-20 00:00:00.000
          2           0 2011-01-18 00:00:00.000 2011-01-20 00:00:00.000
          2           2 2011-01-19 00:00:00.000 2011-01-20 00:00:00.000
          2           1 2011-01-20 00:00:00.000 NULL

(9 row(s) affected)


Ooops, ima dve status na dan 17 januar za ID = 1. Ajde brzo da ispravimo ako moze, daj UPDATE. E tu je kvaka. Ako bi evidenciju vodili na papiru, mnogo je manja verovatnoca da bi napravili ovakvu gresku. Kao prvo, nemoguce je udenuti (INSERT) novi red na pozicju ID=1 17 Januar. Drugo, kad pisemo rukom, manja je verovatnoca da cemo pogresno upisati datum, nego kad kucamo po tastaturi ili kliknemo na kalendar. Koliko sam samo puta uneo pogresan mesec ili pogresnu godinu.... :-( Verovatno je ova situacija dovela do vaznog stava iz Marfijevog Zakona "Gresiti je ljudski, ali da se stvarno zabrlja, potreban je kompjuter".

Kako bi zgledalo resenje koje nije naivno, i zahteva jednostavniji kveri za OdDatuma - DoDatuma? Resenje je u konstrukciji tabele. Nije intuitivno i nije jednostavno, ni za razumavanje, ni za implementaciju, ali je moguce.

Da to uradimo u novom postu.


 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo25.03.2011. u 15:18 - pre 142 meseci
Zidar, samo bih napomenula da je PrethodnoId broj reda (Id) onog koji se menja i ciji status postaje 0 kad se promeni, trebalo bi te neaktivne redove ne moci menjati. PocetniId je da bi se pri menjanju sacuvalo i pomocu querija dobilo istorijat promene po redolsedu promene (odnosno po Id koloni), cak nezavisno od datuma, jer potencijalno mogu postojati izmene a da nije bitan datum kad su izvrsene, i do kad vaze; ali kad je bitno, pod uslovom da je izvrsen korektan unos datuma kako dobiti i datumdo sledece izmene.
Id je identity u funkciji Surrogat key i ne moze se ponavljati, odnosno Id je Primery Key

Nisam bas totalni pocetnik kom treba pomoc, vishe mi je cilj diskusija i razmena misljenja bas zbog drugacijeg pogleda na stvari, konkretno vezano za upotrebu Vestackih kljuceva.

[Ovu poruku je menjao nadavesela dana 25.03.2011. u 16:35 GMT+1]

[Ovu poruku je menjao nadavesela dana 25.03.2011. u 16:36 GMT+1]
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo25.03.2011. u 15:51 - pre 142 meseci
Za pocetak, dodajem dve kolone u tabelu:
Code:
DROP TABLE Status_History
;

CREATE TABLE Status_History
(
ID int NOT NULL  -- ID je id nekog entiteta za koga pratimo promenu statusa
, StatusID int NOT NULL
, DatumOd DateTime NOT NULL
-- Dodajem dve kolone, koje mogu imati NULL
, StariStatus int NULL
, StariDatumOD DateTime NULL
, PRIMARY KEY (ID, statusID, DatumOd)
)
GO


Cemu mi one sluze? Da povezem novi red za prethodni. To je za sada jedina promena u tabeli. Da unesemo pocetni status za oba entiteta.
Code:
-- Za prvi red, nemamo StariStatus niti StariDatumOD pa cemo ih ostaviti da budu NULL
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    1,        0,        '20110117' , NULL        , NULL        )
;

INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    2,        1,        '20110117' , NULL        , NULL        )
;

-- Sta smo dobili
SELECT * FROM Status_History
;
         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          2           1 2011-01-17 00:00:00.000        NULL NULL

(2 row(s) affected)


Unesimo prvu ptromenu statusa za oba entiteta:
Code:
-- Prve promene statusa.
-- Pazljivo unosim StariStatus i StariDatumOd
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    1,        1,        '20110118' ,    0        , '20110117' )
;

INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    2,        0,        '20110118' ,    1        , '20110117'        )

-- dobili smo
SELECT * FROM Status_History
ORDER BY ID, DatumOd
;

         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          1           1 2011-01-18 00:00:00.000           0 2011-01-17 00:00:00.000
          2           1 2011-01-17 00:00:00.000        NULL NULL
          2           0 2011-01-18 00:00:00.000           1 2011-01-17 00:00:00.000

(4 row(s) affected)

Ovde treba da zastanemo i dobro pogledamo sat smo dobili. Novi red za svaki entitet sadrzi podatke iz prethodnog reda. Tako znam tacno koji red dolazi iz koga, ko je kome prethodnik i sledbenik. Za sada, to sam postigao tako sto sam svesno i pazljivo uneo podatke da dobijem ono sto mi treba. Da dodamo jos po jean red i da pokusamo da napravimo onaj upit Oddatuma-Dodatuma.
Code:
 INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    1,        2,        '20110119' ,    1        , '20110118' )
;

INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    2,        2,        '20110119' ,    0        , '20110118'        )
;
-- dobili smo
SELECT * FROM Status_History
ORDER BY ID, DatumOd
;
         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          1           1 2011-01-18 00:00:00.000           0 2011-01-17 00:00:00.000
          1           2 2011-01-19 00:00:00.000           1 2011-01-18 00:00:00.000
          2           1 2011-01-17 00:00:00.000        NULL NULL
          2           2 2011-01-18 00:00:00.000           0 2011-01-18 00:00:00.000
          2           0 2011-01-18 00:00:00.000           1 2011-01-17 00:00:00.000

(6 row(s) affected)

Imamo po tri reda, dovoljno za upit. Pokusajmo upit:
Code:
 
-- Upit koji vraca OD - Do
SELECT 
    ID
    , OdDatuma = StariDatumOD
    , DoDatuma = DatumOD
    , [Status] = StariStatus
FROM Status_History
ORDER BY ID, DatumOD
;

         ID OdDatuma                DoDatuma                     Status
----------- ----------------------- ----------------------- -----------
          1 NULL                    2011-01-17 00:00:00.000        NULL
          1 2011-01-17 00:00:00.000 2011-01-18 00:00:00.000           0
          1 2011-01-18 00:00:00.000 2011-01-19 00:00:00.000           1
          2 NULL                    2011-01-17 00:00:00.000        NULL
          2 2011-01-17 00:00:00.000 2011-01-18 00:00:00.000           1
          2 2011-01-18 00:00:00.000 2011-01-19 00:00:00.000           0

(6 row(s) affected)

Primetite da smo upotrebili ORDER BY. Bez ORDER BY redovi ce se pojaviti u nekom proizvolnom, slucajnom i zbunjujucem redosledu.
Sta vidimo iz rezultata? Prvo, oni redovi gde kolne OdDatuma i Status imaju vrednost NULL nam ne govore nista pa ih treba eliminisati. Evo.
Code:

-- eliminisemo NULL redove
SELECT 
    ID
    , OdDatuma = StariDatumOD
    , DoDatuma = DatumOD
    , [Status] = StariStatus
FROM Status_History
WHERE StariDatumOD IS NOT NULL
ORDER BY ID, DatumOD
;
-- dobijemo:
         ID OdDatuma                DoDatuma                     Status
----------- ----------------------- ----------------------- -----------
          1 2011-01-17 00:00:00.000 2011-01-18 00:00:00.000           0
          1 2011-01-18 00:00:00.000 2011-01-19 00:00:00.000           1
          2 2011-01-17 00:00:00.000 2011-01-18 00:00:00.000           1
          2 2011-01-18 00:00:00.000 2011-01-19 00:00:00.000           0

(4 row(s) affected)  

Vrlo lepo, jedino sto nam se ne vidi poslednji status, onaj tekuci. Izracunajmo tekuci status. U svakom redu imam StaridatumOD i StariStatus, osim za one redove koji su poslednji dodati - koji imaju tekuci status. Dakle, red koji cuva tekuci status za konkretni entitet ID jeste onaj koji nema odgovarajuceg para u skupu parova (ID, StariDatumOD, StariStatus)

Code:

-- Ovo nam daje tekuci status 
SELECT     
    H1.ID
    , OdDatuma = H1.DatumOD
    , DoDatuma = NULL
    , [Status] = H1.StatusID
FROM Status_History AS H1
LEFT JOIN Status_History AS H2 ON H1.ID = H2.ID AND H1.DatumOD = H2.StariDatumOD
WHERE H2.StariDatumOD IS NULL
;

         ID OdDatuma                   DoDatuma      Status
----------- ----------------------- ----------- -----------
          1 2011-01-19 00:00:00.000        NULL           2
          2 2011-01-19 00:00:00.000        NULL           2

(2 row(s) affected)


Konacan kupit je unija od redova koji se dobiajju direktno zi tabele i redova koji cuvaju tekuci status. Evo:

Code:

-- Konacan iskaz:
SELECt X.* FROM
(
-- iz tabele direktno dobijamo
SELECT 
    ID
    , OdDatuma = StariDatumOD
    , DoDatuma = DatumOD
    , [Status] = StariStatus
FROM Status_History
WHERE StariDatumOD IS NOT NULL
UNION ALL
SELECT     
    H1.ID
    , OdDatuma = H1.DatumOD
    , DoDatuma = NULL
    , [Status] = H1.StatusID
FROM Status_History AS H1
LEFT JOIN Status_History AS H2 ON H1.ID = H2.ID 
                            AND H1.DatumOD = H2.StariDatumOD
WHERE H2.StariDatumOD IS NULL
) AS X
ORDER BY ID, OdDatuma, DoDatuma
;

         ID OdDatuma                DoDatuma                     Status
----------- ----------------------- ----------------------- -----------
          1 2011-01-17 00:00:00.000 2011-01-18 00:00:00.000           0
          1 2011-01-18 00:00:00.000 2011-01-19 00:00:00.000           1
          1 2011-01-19 00:00:00.000 NULL                              2
          2 2011-01-17 00:00:00.000 2011-01-18 00:00:00.000           1
          2 2011-01-18 00:00:00.000 2011-01-19 00:00:00.000           0
          2 2011-01-19 00:00:00.000 NULL                              2

(6 row(s) affected)


Znaci, ako bi ovako prenosili vrednosti iz reda u red, kveriji bi bili jednostavni i vladao bi red i mir u tabeli. Jedino, kako da nateramo korisnika da bude pazljiv? Ako probate sami da unesete nekoliko redova, videcete da nije lako.

U novom postu pokazacemo kako i to moze.

 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo25.03.2011. u 17:07 - pre 142 meseci
Podaci vrede onoliko koliko su tacni i bezbedni. Pokusajmo da obezbedimo tacnost podataka. Videcemo kako iz tacnosti proizlazi i dobar deo bezbednosti.
Code:


DROP TABLE Status_History
GO

CREATE TABLE Status_History
(
ID int NOT NULL  -- ID je id nekog entiteta za koga pratimo promenu statusa
, StatusID int NOT NULL
, DatumOd DateTime NOT NULL
, StariStatus int NULL
, StariDatumOD DateTime NULL
, CONSTRAINT PK_Status_History PRIMARY KEY (ID, DatumOd, StatusID)
, CONSTRAINT ck_Status_History_StariDatumPreNovog CHECK (StariDatumOD < DatumOd)
)
GO


Ako hocemo da nam (ID, StariStatus, StariDatumOD) zavisi od prethodnog reda, mozemo da postavimo FOREIGN KEY, tako da StariStatus i StariDatumOD mora da postoji vec u tabeli da bismo ga dodelili novom redu. Ovo neodoljivo podseca na jedan od nacina prikazivanja hijerarhija u relacionim sistemima. I jeste, svaki red je roditelj prethodnom redu. U 'obicnoj' hijerrahiji jedan red moze imati vise dece, a pokazacemo da ovde jedan red ne treb da ima vise od jednog deteta.
Code:

ALTER TABLE Status_History
ADD CONSTRAINT Status_History_vea_Izmedju_Redova
FOREIGN KEY (ID, StariDatumOD, StariStatus )
REFERENCES Status_History (ID,DatumOd,  StatusID )




Sada tabela nece prihvatiti unos u StariStatus ili StariDatumOd koji ne postoje u nekom od prethodnih redova, a sve za posmatrani entitet.
Unesimo neke podatke, ispravne ili neispravne
Code:

-- Za prvi red, nemamo StariStatus niti StariDatumOD pa cemo ih ostaviti da budu NULL
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    1,        0,        '20110117' , NULL        , NULL        )
;
-- Sve je OK -- (1 row(s) affected)
SELECT * FROM Status_History;

         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL

(1 row(s) affected)



Sta ce se desiti ako pokusamo da unesemo nekakv StariStatus i/ili StariDatumOD?
Code:

-- Pocetni status za entitet ID = 2
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    2,        1,        '20110117' ,   0        , '20110112'        )
;
-- nece moci:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the FOREIGN KEY SAME TABLE constraint "Status_History_vea_Izmedju_Redova". The conflict occurred in database "DMA", table "dbo.Status_History".
The statement has been terminated.


FOREIGN KEY nas je spasao od loseg unosa. tek da se zna Medjutim , daleko od toga da smo bezbedni. Pogledajte ovo;
Code:

-- medjutim, nepotpuna kombinacija prolazi, a ne bi trebalo:
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    2,        1,        '20110117' ,   0        , NULL        )
;
-- proslo je, a ne bi trebalo (1 row(s) affected)
SELECT * FROM Status_History;

         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          2           1 2011-01-17 00:00:00.000           0 NULL

(2 row(s) affected)

-- eliminsacu red za Id = 2, da nam ne smeta u daljem testiranju. 
-- Treba nam nesto da sprecimo unos 'starih' podataka za prvi red entiteta.

DELETE Status_History
WHERE ID = 2

SELECT * FROM Status_History;

         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL

(1 row(s) affected)


Mozemo reci ovako pravilo: StariStatus i StariDatumOD su ili ona NULL ili oba su NOT NULL. To bi sprecilo anomalije koje smo upravo videli.
Code:

ALTER TABLE Status_History
ADD CONSTRAINT ck_Status_History_StariStatusDatumOD_NULLability
CHECK (
        (StariStatus IS NULL AND StariDatumOD IS NULL)
        OR
        (StariStatus IS NOT NULL AND StariDatumOD IS NOT NULL)
        )


-- Pokusajmo nekompletan unos za StariStatus StariDatumOD, 
-- Prokusajmo isti INSERT koji je bio prosao, a nije trebao
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    2,        1,        '20110117' ,   0        , NULL        )
;
-- Ovaj put nije proslo:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "ck_Status_History_StariStatusDatumOD_NULLability". The conflict occurred in database "DMA", table "dbo.Status_History".
The statement has been terminated.


-- Ovo naravno prolazi 
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    2,        1,        '20110117' ,   NULL        , NULL        )
;
-- (1 row(s) affected)

SELECT * FROM Status_History;

         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          2           1 2011-01-17 00:00:00.000        NULL NULL

(2 row(s) affected)


Pokusajmo jos nekoliko redova:
Code:

-- DatumOd = Staridatum, ocekujem gresku
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD,    StariStatus, StariDatumOD)
VALUES    (    1,    1        ,    '2011-01-17',    0,        '2011-01-17')
;
-- Naravno da ne moze:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "ck_Status_History_StariDatumPreNovog". The conflict occurred in database "DMA", table "dbo.Status_History".
The statement has been terminated.

-- DatumOd < Staridatum, StariStatus izpravan
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD        ,    StariStatus, StariDatumOD)
VALUES    (    1,    1        ,    '2011-01-18',        0,        '2011-01-17')
;
-- naravno da prolazi

SELECT * FROM Status_History ORDER BY ID, DatumOD
;

         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          1           1 2011-01-18 00:00:00.000           0 2011-01-17 00:00:00.000
          2           1 2011-01-17 00:00:00.000        NULL NULL

(3 row(s) affected)


FOREIGN KEY smo vec testirali, ne dozvoljava da se unese nepostojeca kombinacija (ID, StariStatus, StariDatumOD). Medjutim, jos uvek mogu da unesem pogresan podatak. Evo:

Code:

-- datumi su OK, Stari status postoji, ali nije dobar, 
-- trebalo bi da bude StariStatus = 1
-- jer  poslednji uneti red za ID=1 ima StatusID = 1
-- Ovo prolazi, a ne bi trebalo:
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD        ,    StariStatus, StariDatumOD)
VALUES    (    1,    3        ,    '2011-01-19',        0,        '2011-01-17')
;
-- proslo je, a ne bi trebalo -- (1 row(s) affected)

SELECT * FROM Status_History ORDER BY ID, DatumOD

         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          1           1 2011-01-18 00:00:00.000           0 2011-01-17 00:00:00.000
          1           3 2011-01-19 00:00:00.000           0 2011-01-17 00:00:00.000
          2           1 2011-01-17 00:00:00.000        NULL NULL

(4 row(s) affected)



Za ID =1, StaiStaus ima vrednost 0 za dva reda, a ne bi trebalo. Ako malo bolje razmislimo, kombinacija (ID, StariStatus, StariDatumOD) treba da je UNIQUE. U tom slucaju, svaki red moze da ima najvise jednog sledbenika.Ovo je srce resenja. Vec znamo da svaki red ima tacno jednog roditelja - FK se brine o tome, kao u hijerarhiji. Ako istovremeo svaki red moze da ima najvise jednog sledbenika i svaki red ima tacno jednog prethodnika, ond gar antujemo tacan redosled i neprekidnost istorijskog niza. A to je ono sto smo hteli da psotignemo na pocetku.

Code:

-- Obrisacu poslednji red, pa da postavimo jos jedno ogranicenje:
DELETE Status_History
WHERE ID=1 AND DatumOd = '2011-01-19'

-- Opet imamo ciste podatke:
SELECT * FROM Status_History ORDER BY ID, DatumOD


         ID    StatusID DatumOd                 StariStatus StariDatumOD
----------- ----------- ----------------------- ----------- -----------------------
          1           0 2011-01-17 00:00:00.000        NULL NULL
          1           1 2011-01-18 00:00:00.000           0 2011-01-17 00:00:00.000
          2           1 2011-01-17 00:00:00.000        NULL NULL

(3 row(s) affected)


Ovo je kljucno granicenje. Uz prethodno postavljene uslove ovim kompletiramo zadatak.
Code:

-- Dodajmo ogranicenje:
ALTER TABLE Status_History
ADD CONSTRAINT unique_StariDatumOD_StariStatus 
UNIQUE (ID, StariDatumOD, StariStatus )    -- iste kolone, isti redosled kao u FK

Ovo resenje sa UNIQUE ogranicenjem vazi samo za MS SQL. MS SQL dozvoljava NULL vrednosti u UNIQUE ogranicenju, ali najvise jednu NULL vrednost, sto nama i treba. SQL standard dozvoljava visestruke NULL vrednosti i UNIQUE ogranicenjima, MS SQL ovde odstupa od standarda, ali nam to olaksava zivot u ovom slucaju. U relacionim sistemima koji u ovom slucaju prate ANSI standard za SQL ovo se mora resiti nekako drugacije. Nije nemoguce, cak smo to u Accesu uspeli da napravimo. Ako moze u Accessu, moze sigurno i u ORACLE ili Postgress ili Firebird. To ce neko iz te ekipe da razradi.

Da testiramo sta imamo:
Code:

-- Pokusajmo sada da unesmo pogresan podatak:
-- datumi su OK, Stari status postoji, ali nije dobar, 
-- trebalo bi da bude StariStatus = 1
-- jer  poslednji uneti red za ID=1 ima StatusID = 1
-- Ovo  ne bi trebalo: da prodje ovog puta:
INSERT INTO Status_History 
        (    ID, StatusID,    DatumOD        ,    StariStatus, StariDatumOD)
VALUES    (    1,    3        ,    '2011-01-19',        0,        '2011-01-17')
;
-- i nije proslo:
Msg 2627, Level 14, State 1, Line 1
Violation of UNIQUE KEY constraint 'unique_StariDatumOD_StariStatus'. Cannot insert duplicate key in object 'dbo.Status_History'.
The statement has been terminated.


Tako se prati istorija promene odabranog atributa za odabrani entitet. U ovom slucaju atribut je bio StatusID, entitet je bilo sta, predstavljeno kolonom ID. DatumOd i StariDatumOd pripadaju metodi i oni ce uvek biti priosutni, bez obzira na entitet i atribut. Entitet ne mora da bude predstavljen jednom kolonom, moze biti i slozeni kljuc.



 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo25.03.2011. u 17:16 - pre 142 meseci
Na ovaj pricnip se mogu nadgraditi i druge korisne stvari. Na primer:

1) dodavanjem tabele VlaidacijaPromeneStausa mozemo definisati dozvoljene parove (StariStatus, NoviStatus). Ta tabela kontrolise sta mozemo uneti u istoriju promene statusa. Ako iznajmljujemo automobile, statusi mogu biti na primer 'nabavljen', 'iznajmljen','na raspolaganju'otpisan' a dozvoljeni parovi su onda

('NULL','Nabavljen')
('nabavljen', 'na raspolaganju') -- mozemo ga ponuditi na iznajmlibanje nekome tek posto smo ga nabavili
('na raspoaganju','iznajmljen') -- mozenmo ga iznajmiti samo ako je na raspolaganju
('iznajmljen', 'na raspoaganju') -- ko ga pozajmi, obicno ga i vrati, i onda je ponovo na raspolaganju
('iznajmljen', 'otpisan') -- ponekad se ajto iznajmi pa se polupa i onda ga otpisemo
('na raspoaganju', 'otpisan') -- iako nije polupan, kad zastari, onda se otpisuje

Nesto slicno smo odradili u Accesu u temi o pracenju osnovnih sredstava (kod koga je bager?)

2) ako je entitet BankovniRacun i atribut IznosTransakcije (+ za ulaz, - za izlaz) mozemo dodati kolonu Stanje u tabelu Status_History koja je jednaka Stanje = PrethodnoStanje + IznosTransakcije. Tada mozemo dodati ogranicenje da Stanje bude uvek vece ili jednako od nule. Ovim smo ispunili san svih onih koji zele da u tabeli cuvaju stanje racuana i azurairaju ga posle svake transakcije.

Slucaj 1) sa validacijom para (StariStatus, NoviStatus) je veom znacajan jer otvara vrata za stvari o kojima do sada nismo mogli ni da sanjamo. A sve bez i jedne linije proceduralnog koda.

Kad unesete gomilu redova u ovako koncipiranu istorijsku tabelu, ispostavlja se da UPDATE jednostavno ne radi vise, zbog FOREIGN KEY. A ni masovno brisanje nece ici, mozete da obristet za svaki netitet samo poslednji red. Znaci, u normalnim uslovima brisanje je veoma tesko. A sve to bez igranaj sa privilegijama i igranja GRANT komandama. Ovo sprecavanje UPDATE i masovnog brisanja i nije tako naivna stvar, ko se nije opekao na ovome bar jednom u zivotu? Najveca opasnost za podatke su programeri koji njima rukuju iza scene, Korisnika mzoete front endom da kontorlisete gotovo 100%, ali programera ne bas tako lako. E ovde se tabela sam stiti.

Dugo sam imao zelju da napisem ovakav clanak negde. Hvala Nadi na pametno poatavljenom pitanju. Prinajem da sam ocekivao nesto ovako.

Ima li pitanja?


 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo28.03.2011. u 08:51 - pre 142 meseci
'I jeste, svaki red je roditelj prethodnom redu. U 'obicnoj' hijerrahiji jedan red moze imati vise dece, a pokazacemo da ovde jedan red ne treb da ima vise od jednog deteta.'

Samo bih napomenula i da ovde postoje 'ogranicenja'...Sta ako se prati istorija promene organizacione strukture...kad dva (ili vishe) odeljenja se fuzioniraju u novo odeljenje....Moze se odluciti da je novo odeljenje novi entitet, a da se kao prethodnik izabere samo jedan od prethodnih.
Druga situacija je kad se jedno odeljenje deli na dva (ili vishe) i kad sva nova odeljenja za prethodnika imaju jedan isti red.
 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo28.03.2011. u 08:57 - pre 142 meseci
'Tako se prati istorija promene odabranog atributa za odabrani entitet. U ovom slucaju atribut je bio StatusID, entitet je bilo sta, predstavljeno kolonom ID. DatumOd i StariDatumOd pripadaju metodi i oni ce uvek biti priosutni, bez obzira na entitet i atribut. Entitet ne mora da bude predstavljen jednom kolonom, moze biti i slozeni kljuc.'
Ovde bih samo upitala, dali ako pratim dva (ili vishe) atributa kao promenu jednog zapisa trebam imati i StariAtribute1, StariAtribut2,....) Zato sam vishe za vestacke kljuceve, gde mi je PrethodniId zajednicki atribut.
 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo28.03.2011. u 09:03 - pre 142 meseci
'Najveca opasnost za podatke su programeri koji njima rukuju iza scene, Korisnika mzoete front endom da kontorlisete gotovo 100%, ali programera ne bas tako lako. E ovde se tabela sam stiti.'
E ovome treba teziti iz starta, jos iz pocetnog dizajna tabela ( za sta se cesto nema 'vremena')
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo28.03.2011. u 16:08 - pre 142 meseci
Citat:
Samo bih napomenula i da ovde postoje 'ogranicenja'...Sta ako se prati istorija promene organizacione strukture...kad dva (ili vishe) odeljenja se fuzioniraju u novo odeljenje....Moze se odluciti da je novo odeljenje novi entitet, a da se kao prethodnik izabere samo jedan od prethodnih.
Druga situacija je kad se jedno odeljenje deli na dva (ili vishe) i kad sva nova odeljenja za prethodnika imaju jedan isti red.

Jos jedno odlicno zapazanje. Ovaj metod, ovako kako je izlzen do sada, generalno govoreci, NE MOZE se upotrebiti za pravcenje promene same hijerarhijske strukture, bolje receno, nije do sada pokazano da moze da prati i promenu strukture same hijerarhije. Izlozeni metod prati promene jednog izbranog atributa kroz vreme. Pomenuo sam hijerarhiju da bih nekako ucuinio pricu shvatljivijom.

Citat:
Ovde bih samo upitala, dali ako pratim dva (ili vishe) atributa kao promenu jednog zapisa trebam imati i StariAtribute1, StariAtribut2,....) Zato sam vishe za vestacke kljuceve, gde mi je PrethodniId zajednicki atribut.

Verovatno moze da se prati vise atributa odjednom, nisam probao niti sam videop da je neko drugi probao, sto ne znaci da ne moze. Mzda nekkao ovako: Verovatno to treba StariAtribuy1 i StariAtribut2 i stariArtibutN i svi oni ulaze u PK ( to je kao neki super-key). Pratis na primer promenu licnih podataka (ime, prezime, telefon). Tabela bi izgledala ovako nekako, u pseudo kodu:

LicniPodaci_History
(
StudentID, VaziOdDatuma, Ime, Prezime, Telefon svi NOT NULL
, StariDatum, StaroIme, StaroPrezime, StariTelefon svi NULL
, PK (StudentID, VaziOdDatuma, , Ime, Prezime, Telefon)
, UNIQUE (StudentID, StariDatum, StaroIme, StaroPrezime, StariTelefon )
, FOREIGN KEY StudentID, StariDatum, StaroIme, StaroPrezime, StariTelefon
REFERENCES LicniPodaci_History (StudentID, VaziOdDatuma, , Ime, Prezime, Telefon)
, CHECK (sta oga sme da bude NULL i kako se odnosi prema drugim kolonama)
)

Ovde treba videti kako medjusobno stoje atributi koje pratimo, da li su NULL ili nisu NULL.

Opisana tehnika je prilicno nova. Prvi clanci o tome pojavili su se u Oktobru 2010, Joe Chelko ih je napisao. Zatim i Alex Kuznetsov, negde oko Nove godine 2011. Tu negde se pominje i knjiga "Applied Mathematics for Database professionals", autori Lex de Haan and Toon Koppelaars. Takodje se pominje i knjiga "Defensive Database programming with SQL Server, Alex Kuznetsov". Ja sam uspeo obe knjige da nadjem kao PDF na iternetu. Prve je kompletna, a iz Alexove nedostaju dva poslednja poglavlja (koaj se ne bave oviom temom). To je sve sto za sada postoji objavljeno o ovoj metodologiji. Ovo je vlajda jedan od prvih clanaka na SH jeziku, mislim da smo nesto slicno pisali na Access forumu pre mesec dana.

Ovo je sve novo i vrata su tek odskrinuta. Ima puno toga da se uradi i proba. Koga zanima, napred, pa kad zapnete, tu smo da pomognemo, ako umemo. Promena strukture hijerarhije - tezak zadatak, mozda se moze, a mozda i ne moze resiti postojecim alatima. Treba probati.

Teorijski znacaj ove 'metodologije' nije tako veliki kao recimo uvodjenej FOREIGN KEYS. Prakticno, medjutim, mozda je ovo i znacajnije jer znacajno smanjije potrebu za procedralnim kodom.

Ko pita prava pitanja, mozda ce i naci odgovor. Ko ne pita, sigurno nece.



 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
*.205.8.137.robi.com.mk.



+3 Profil

icon Re: Computed DatumDo28.03.2011. u 21:02 - pre 142 meseci
Mozda mnogi od nas naterani praksom imamo potrebu primene te nove 'metodologije', i koliko god da je uspesno primenjena ili ne, mislim da je njena glavna ideja sustinska i ne uci se, jer cini mi se ko intiuitvno prihvati taj je i intuitivno bio prinudjen da je otkrije, ali ohrabruje kad ima clanaka i knjiga za 'propagiranje', 'objasnjenje' i 'perfekcioniranje' iste.
 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo04.04.2011. u 14:48 - pre 141 meseci
Zidar evo kako sam spojila dve metodologije (vestacke kljucevi, i istorija izmena). Za constrinte sam koristila UDF, a StatusId, i StariStatus su mi identifikatori novog i prethodnog reda. StariDatumOd mi je ipak ComputedColona pomocu UDF. Ovako mogu upisivati novi red ako mi je promenjen bar neki od atributa (cena, naziv,...), i mogu fleksibilno menjati uslove izmene. Postoji neki proceduralni kod, al je na nivou constrainta tabele (UDF) a ne u stored proceduri.
Mislim da je vecina zahteva iz 'tvog' clanka ispunjena, jedino ako nesto ne previdjam.

Tabela:
Code:

CREATE TABLE [dbo].[Status_HistoryES](
    [ID] [int] NOT NULL,
    [StatusID] [int] IDENTITY(1,1) NOT NULL,
    [DatumOd] [datetime] NOT NULL,
    [StariStatus] [int] NULL,
    [cena] [decimal](15, 2) NOT NULL,
    [naziv] [nvarchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 CONSTRAINT [PK_Status_HistoryES] PRIMARY KEY CLUSTERED 
(
    [StatusID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY],
 CONSTRAINT [unique_StariStatusES] UNIQUE NONCLUSTERED 
(
    [ID] ASC,
    [StariStatus] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]


REFERENCA izmedju StatusID i StariStatus
Code:

ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK ADD  CONSTRAINT [FK_Status_HistoryES_Status_HistoryES] FOREIGN KEY([StariStatus])
REFERENCES [dbo].[Status_HistoryES] ([StatusID])



UDF:
Code:

CREATE FUNCTION [dbo].[fn_StariStatus](@Id AS int,@StatusId as int)
RETURNS int
AS
BEGIN
DECLARE @starstatus as int
SET @starstatus=(select max(StatusId) as starstatus from dbo.Status_HistoryES where [email protected] and StatusId<@StatusId)
RETURN (@starstatus)
END


Code:

create FUNCTION [dbo].[fn_StariDatum](@Id AS int,@StatusId as int)
RETURNS DATETIME
AS
BEGIN
DECLARE @datumod as datetime
SET @datumod=(select max(Datumod) as datumod from dbo.Status_HistoryES where [email protected] and StatusId<@StatusId)
RETURN (@datumod)
END


Code:

CREATE FUNCTION [dbo].[fn_StariDatumOd](@StariStatus as int,@Id AS int)
RETURNS datetime
AS
BEGIN
DECLARE @datumdo as datetime
SET @datumdo=(select min(DatumOd) as datumvreme from dbo.Status_History 
--where StariStatusComputed =(select min(StariStatus) as StariStatus from dbo.Status_HistoryES where [email protected] AND [email protected]))
where StariStatus =(select min(StariStatus) as StariStatus from dbo.Status_HistoryES where [email protected] AND [email protected]))

RETURN (@datumdo)
END

Code:

CREATE FUNCTION [dbo].[fn_StaraCena](@Id AS int,@StatusId as int)
RETURNS decimal(15,2)
BEGIN
DECLARE @cena as decimal(15,2)
SET @cena=(select cena from dbo.Status_HistoryES where [email protected] and StatusId=dbo.fn_StariStatus(@Id,@StatusId))
RETURN (@cena)
END

Code:

CREATE FUNCTION [dbo].[fn_StariNaziv](@Id AS int,@StatusId as int)
RETURNS nvarchar(50)
AS
BEGIN
DECLARE @naziv as nvarchar(50)
SET @naziv=(select naziv from dbo.Status_HistoryES where [email protected] and StatusId=dbo.fn_StariStatus(@Id,@StatusId))
RETURN (@naziv)
END



Constrainti:
Code:

ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK ADD  CONSTRAINT [CK_CenaNazivStatus_HistoryES] CHECK  ((isnull([dbo].[fn_StaraCena]([Id],[StatusId]),(0))<>[Cena] OR isnull([dbo].[fn_StariNaziv]([Id],[StatusId]),(0))<>[naziv]))
GO
ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK ADD  CONSTRAINT [CK_DatumOdStatus_HistoryES] CHECK  (([dbo].[fn_StariDatum]([Id],[StatusId])<=[DatumOd]))
GO
ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK ADD  CONSTRAINT [CK_StariStatusStatus_HistoryES] CHECK  ((isnull([dbo].[fn_StariStatus]([Id],[StatusId]),(0))=[StariStatus]))










[Ovu poruku je menjao nadavesela dana 05.04.2011. u 09:19 GMT+1]

[Ovu poruku je menjao nadavesela dana 05.04.2011. u 13:32 GMT+1]
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo04.04.2011. u 22:13 - pre 141 meseci
Bravo ! Deluje zanimljivo.

Nadam se da cu stici da detalljnije proanaliziram resenje do kraja nedelje. Deluje zanimljivo. U medjuvremenu, demonstriraj nam ne nekoliko primera sta se prati i kako radi. Bice nam svima lakse da shvatimo sta se desava. Daj neke test podatke, da imamo s cim da radimo.

 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo05.04.2011. u 08:40 - pre 141 meseci
CREATE PROCEDURE [dbo].[TestPodaci]
@Id as int,@datumod as datetime,@StariStatus as int,@cena as decimal(15,2),@naziv nvarchar(50)
AS
BEGIN
INSERT INTO dbo.Status_HistoryES(Id,datumod,StariStatus,cena,naziv)
VALUES (@Id,@datumod,@StariStatus,@cena,@naziv)
END

ako bi se unos izvrsavao bez greske
posto ne postoji prethodni zapis za entitet Id=1, input Parametar StariStatus ima vrednost NULL
1:
Code:

execute dbo.TestPodaci 1,'01/01/2011',NULL,100.00,'P1'

posle ovog unosa StatusId=1
Novim zapisom se vrsi izmena tog reda i input parametar StariStatus ima vrednost 1
2:
Code:

execute dbo.TestPodaci 1,'02/01/2011',1,200.00,'P1'

posle ovog unosa StatusId=2 i izmena tog reda podrazumeva novi Insert ciji je parametar StariStatus=2
3:
Code:

execute dbo.TestPodaci 1,'03/01/2011',2,200.00,'P2'

posle ovog unosa StatusId=3 i izmena tog reda podrazumeva novi Insert ciji je parametar StariStatus=3
4:
Code:

execute dbo.TestPodaci 1,'04/01/2011',3,300.00,'P3'

posle ovog unosa StatusId=4 i izmena tog reda podrazumeva novi Insert ciji je parametar StariStatus=4
5:
Code:

execute dbo.TestPodaci 1,'05/01/2011',4,200.00,'P1'

posle ovog unosa StatusId=5 i izmena tog reda podrazumeva novi Insert ciji je parametar StariStatus=5
ovaj insert ima ponavljanje vec prethodno unesenih vrednosti (u 2:) za cenu=200 i naziv='P1' ali to su nove vrednsti u odnosu na poslednji uneseni red (4:) koji se menja (sa 5:)
pri svim insertima treba biti zadovoljen uslov da datumod mora biti >= od Datuma od reda koji se menja
novi insert je ispravan i ako je bar jedan od atributa (cena ili naziv) se razlikuje od vrednosti atributa reda koji se menja

za novi entitet Id=2 evidencija pravilnog unosa bi bila, tu je moguce uneti istu cenu i naziv kao i prethodno za entitet Id=1, ali mozda je i to dobro, ako neko zeli da te izmene prati kao novi entitet.
Code:
execute dbo.TestPodaci 2,'01/01/2011',NULL,100.00,'P1'

Code:
execute dbo.TestPodaci 2,'02/01/2011',6,200.00,'P1'

Code:
execute dbo.TestPodaci 2,'03/01/2011',7,200.00,'P2'

Code:
execute dbo.TestPodaci 2,'04/01/2011',8,300.00,'P3'

ponovna izmena entiteta Id=1 znaci izmenu reda ciji je StatusId=5 i izmena tog reda podrazumeva novi Insert ciji je parametar StariStatus=5
Code:
execute dbo.TestPodaci 1,'06/01/2011',5,300.00,'P1'



Moji primeri su idealni...svako moze nesumice uneti sto on hoce, pa da vidi kako tabela sa constraintima reaguje.
ono sto mi se malo ne dopada je sto pri greskama StatusId (koji je Identity) se povecava, sto predpostavljam se moze resiti na nivou stored procedure.
dala sam samo neki pocetne primere....potrudicu se da jos detaljnije objasnim (ako neko uopste cita, da ne bude da objasnjavam samoj sebi)



[Ovu poruku je menjao nadavesela dana 05.04.2011. u 13:21 GMT+1]
 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo05.04.2011. u 12:45 - pre 141 meseci
Nakon unosa test podataka kreirana je computed kolona StariDatumOd sa formulom: ([dbo].[fn_StariDatumOd]([StatusId],[Id]))
Tako da je za svaki red moguce na nivou tabele odredjen DatumOd koga vazi do datuma do kog vazi (StariDatumDo)

 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo05.04.2011. u 16:52 - pre 141 meseci
Pogledao sam resenje detaljnije i vide se prve pukotine. Pazi, nije bio cilj da se u tabeli pokaze interval vazenja para (Cena,Naziv). Cilj je bio da se grantuje da ce niz datuma biti neprekidan, da se garantuje da je rd izveden iz tacno jednog reda koji ima stariji datum.

Na osnovu tvojih skripti, uspeo sam da napravim tabelu i sva ogranicenja, i racunsku kolonu. Stored procedura mi ne treba za unos, obican INSERT INTO zavrsava posao.
Code:

IF Object_ID('Status_HistoryES') IS NOT NULL DROP TABLE Status_HistoryES
go
IF Object_ID('fn_StariStatus') IS NOT NULL DROP FUNCTION fn_StariStatus
go
IF Object_ID('fn_StariDatumOd') IS NOT NULL DROP FUNCTION fn_StariDatumOd
go
IF Object_ID('fn_StaraCena') IS NOT NULL DROP FUNCTION fn_StaraCena
go
IF Object_ID('fn_StariNaziv') IS NOT NULL DROP FUNCTION fn_StariNaziv
go

CREATE TABLE [dbo].[Status_HistoryES](
    [ID] int NOT NULL,
    [StatusID] int IDENTITY(1,1) NOT NULL,
    [DatumOd] smalldatetime NOT NULL,
    [StariStatus] int NULL,
    [cena] smallmoney  NOT NULL,
    [naziv] varchar(5) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
 CONSTRAINT [PK_Status_HistoryES] PRIMARY KEY CLUSTERED 
(
    [StatusID] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY],
 CONSTRAINT [unique_StariStatusES] UNIQUE NONCLUSTERED 
(
    [ID] ASC,
    [StariStatus] ASC
)WITH (IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
go
ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK 
ADD  CONSTRAINT [FK_Status_HistoryES_Status_HistoryES] FOREIGN KEY([StariStatus])
REFERENCES [dbo].[Status_HistoryES] ([StatusID])
go
CREATE FUNCTION [dbo].[fn_StariStatus](@Id AS int,@StatusId as int)
RETURNS int
AS
BEGIN
DECLARE @starstatus as int
SET @starstatus=(select max(StatusId) as starstatus 
from dbo.Status_HistoryES
 where [email protected] and StatusId<@StatusId)
RETURN (@starstatus)
END
go
--DROP FUNCTION fn_StariDatumOd

CREATE FUNCTION [dbo].[fn_StariDatumOd](@StariStatus as int,@Id AS int)
RETURNS datetime
AS
BEGIN
DECLARE @datumdo as datetime
SET @datumdo=(select min(DatumOd) as datumvreme from dbo.Status_HistoryES 
--where StariStatusComputed =(select min(StariStatus) as StariStatus from dbo.Status_HistoryES where [email protected] AND [email protected]))
where StariStatus =(select min(StariStatus) as StariStatus from dbo.Status_HistoryES where [email protected] AND [email protected]))
RETURN (@datumdo)
END
go
CREATE FUNCTION [dbo].[fn_StaraCena](@Id AS int,@StatusId as int)
RETURNS decimal(15,2)
BEGIN
DECLARE @cena as decimal(15,2)
SET @cena=(select cena from dbo.Status_HistoryES where [email protected] and StatusId=dbo.fn_StariStatus(@Id,@StatusId))
RETURN (@cena)
END
go
CREATE FUNCTION [dbo].[fn_StariNaziv](@Id AS int,@StatusId as int)
RETURNS nvarchar(50)
AS
BEGIN
DECLARE @naziv as nvarchar(50)
SET @naziv=(select naziv from dbo.Status_HistoryES where [email protected] and StatusId=dbo.fn_StariStatus(@Id,@StatusId))
RETURN (@naziv)
END
go
ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK 
ADD  CONSTRAINT [CK_CenaNazivStatus_HistoryES] 
CHECK  ((isnull([dbo].[fn_StaraCena]([Id],[StatusId]),(0))<>[Cena] OR isnull([dbo].[fn_StariNaziv]([Id],[StatusId]),(0))<>[naziv]))
GO
ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK 
ADD  CONSTRAINT [CK_DatumOdStatus_HistoryES] 
CHECK  (([dbo].[fn_StariDatumOD]([Id],[StatusId])<=[DatumOd]))    -- izmena, bilo je 'fn_StariDatum'
GO
ALTER TABLE [dbo].[Status_HistoryES]  WITH CHECK 
ADD  CONSTRAINT [CK_StariStatusStatus_HistoryES] 
CHECK  ((isnull([dbo].[fn_StariStatus]([Id],[StatusId]),(0))=[StariStatus]))
GO

ALTER TABLE Status_HistoryES
ADD StariDatumOd  AS ([dbo].[fn_StariDatumOd]([StatusId],[Id]))
go


Onda sam uneo prvi test podatak. za artkl ciji je ID=1 uneo sam cenu i naziv:
Code:
INSERT INTO dbo.Status_HistoryES(Id, datumod    ,StariStatus ,cena  ,naziv)
                        SELECT  1, '01/01/2011',NULL       ,100.00  ,'P1';

SELECT * FROM Status_HistoryES
;
(1 row(s) affected)
         ID    StatusID DatumOd                 StariStatus                  cena naziv StariDatumOd
----------- ----------- ----------------------- ----------- --------------------- ----- -----------------------
          1           1 2011-01-01 00:00:00            NULL                100.00 P1    NULL

(1 row(s) affected)

Zatim sam pokusao da unesem nekoliko redova odjednom. Uspeva samo ako znas koji je status poslednji za posmatrani artikl. U visekorisnckom okruzenju ovo moze da predstavlja problem, ako dva korisnika menaju podatke za isti artikl istovremeno. Ne bi trebalo tako da bude, ali se ne mzoemo oslanjati na 'ne bi trebalo...'. Ovako je islo:
Code:

INSERT INTO dbo.Status_HistoryES(Id, datumod    ,StariStatus ,cena  ,naziv)
SELECT ID = 1, datumod = '2011/01/16', StariStatus = 1, Cena = 100.00, Naziv = 'P2'
UNION    -- ovde moram da pogadjam StariStatus
SELECT ID = 1, datumod = '2011/01/18', StariStatus = 2, Cena = 102.00, Naziv = 'P2'
UNION    -- ovde moram da pogadjam StariStatus
SELECT ID = 1, datumod = '2011/01/18', StariStatus = 3, Cena = 103.00, Naziv = 'P3'
;
SELECT * FROM Status_HistoryES
;
         ID    StatusID DatumOd                 StariStatus                  cena naziv StariDatumOd
----------- ----------- ----------------------- ----------- --------------------- ----- -----------------------
          1           1 2011-01-01 00:00:00            NULL                100.00 P1    2011-01-16 00:00:00.000
          1           2 2011-01-16 00:00:00               1                100.00 P2    2011-01-18 00:00:00.000
          1           3 2011-01-18 00:00:00               2                102.00 P2    2011-01-18 00:00:00.000
          1           4 2011-01-18 00:00:00               3                103.00 P3    NULL

(4 row(s) affected)

-- StariDatumOd u stvari pokazuje 'Vazi Do'
-- Imamo dve promene na isti dan, 18 januar 2011. Moze da bude OK, a i ne mora.

Onda sam pokusao da unesem neke pogresne podatke.
Code:

-- slucaj 1: nema promene u (Cena,Naziv), menja se samo StatusID
INSERT INTO dbo.Status_HistoryES(Id, datumod    ,StariStatus ,cena  ,naziv)
SELECT ID = 1, datumod = '2011/01/19', StariStatus = 4, Cena = 103.00, Naziv = 'P3'
-- DObro, spreceno je:
/*
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "CK_CenaNazivStatus_HistoryES". The conflict occurred in database "Test", table "dbo.Status_HistoryES".
The statement has been terminated.
*/

Sad sam pokusao da unesem pogresan datum. Poslednji uneti datum je bio 18 Januar 2011. Sledeci datum moze biti samo isti ili veci od toga. Stio se mene tice, trebalo bi da bude strogo veci, ali da dopustimo mogucnost vece ili jednako. Datum raniji od 18 Januara ne bi smeo da se dozvoli. Zasto? Pa ako smo napravili neke fakture u periodu 01-16 Januara, ocekujemo da na njima pise cena koja je vazila od 1 Januara. pazie sad ovo:
Code:
-- Slucaj 2: pogresan datum, raniji od dosada unetih
-- STa ako sada unesem neki potpuno pogresan datum, recimo 10 januar 2011?
INSERT INTO dbo.Status_HistoryES(Id, datumod    ,StariStatus ,cena  ,naziv)
SELECT ID = 1, datumod = '2011/01/10', StariStatus = 4, Cena = 104.00, Naziv = 'P4'
-- proslo je a ne bi trebalo da prodje!

SELECT * FROM Status_HistoryES
;

         ID    StatusID DatumOd                 StariStatus                  cena naziv StariDatumOd
----------- ----------- ----------------------- ----------- --------------------- ----- -----------------------
          1           1 2011-01-01 00:00:00            NULL                100.00 P1    2011-01-16 00:00:00.000
          1           2 2011-01-16 00:00:00               1                100.00 P2    2011-01-18 00:00:00.000
          1           3 2011-01-18 00:00:00               2                102.00 P2    2011-01-18 00:00:00.000
          1           4 2011-01-18 00:00:00               3                103.00 P3    2011-01-10 00:00:00.000
          1           6 2011-01-10 00:00:00               4                104.00 P4    NULL

(5 row(s) affected)

-- Pogledaj red gde je StatusID = 4, pise da je (cena,naziv) vazila od 18 januara do 10 januara
-- Za statusID = 6 imamo da vazi od datuma 10 januar 2011. 


Ako se dozvoli naknadan unos datuma 10 Januar, onda ce se na stavkama fakture izdate na dan i posle 10 Januara iznenada promeniti cenu i/ili naziv artikala. Posto je faktura vec odstampana i poslata kupcu, sramota je da sada menjamo uslove, mogao bi kupac da nas tuzi ili da izgledamo neposlovno. To je svrha pracenja promena kroz vreme - da se ne dozvolje promene unazad. Ako je svrha samo da se upise kad su se desile promene, sa pretpostavkom da je korisnik to odradio tacno, onda nam one komplikovane funkcije i ogranixcenja i ne trebaju.

Ovde smo imali samo dva atributa ciju promenu pratimo. Izbegli smo kreiranje novih kolona, ali smo umesto novie tri kolone dobili nove tri funkcije. FK smo napravili svejedno, samo je maskiran FK kao CHECK koji gleda u funkciju. Sve u svemu, suvise komplikovano, za samo dve kolone.

Nacin koji sam opisao postize ovo sa mnje pisanja. Novi post.





[Ovu poruku je menjao Zidar dana 05.04.2011. u 18:11 GMT+1]
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo05.04.2011. u 17:10 - pre 141 meseci
Evo kako je moglo:
Code:

CREATE TABLE PromeneImenaIcene
-- primer tabele gde se prate promene dva atributa
-- Za vise tributa, povecava se PK i UN_StariPodaci, dodavanjem novih kolona
-- takodje, u CK_StariPodaci_su_NULL_na_pocetku treba dodati odgovarajuce nove kolne
(
    ArtiklID int NOT NULL
    , Naziv varchar(5) NOT NULL
    , Cena money NOT NULL
    , VaziOd datetime NOT NULL
    , STariDatum datetime NULL
    , StaraCena money NULL
    , StariNaziv varchar(5)
    , CONSTRAINT PK_PromeneImenaIcene 
                PRIMARY KEY (ArtiklID, VaziOD, Cena, Naziv)
    , CONSTRAINT UN_StariPodaci 
                UNIQUE (ArtikLID, Staridatum, StaraCena, StariNaziv)
    , CONSTRAINT CK_StariDatum_Stariji_od_Novog 
                CHECK((StariDatum < VaziOd OR STaridatum IS NULL))
    , CONSTRAINT CK_StariPodaci_su_NULL_na_pocetku 
                CHECK (
                        (Staridatum IS NULL AND StaraCena IS NULL 
                                AND  StariNaziv IS NULL)
                        OR
                        (Staridatum IS NOT NULL AND StaraCena IS NOT NULL 
                                AND  StariNaziv IS NOT NULL)
                        )
)
;


Malo testiranja:
Code:

-- za prvi red o nekom artiklu, stari podaci su svi NULL
INSERT INTO PromeneImenaIcene (ArtiklID, Naziv, Cena, VaziOd, STariDatum,  StaraCena, StariNaziv)
                            SELECT ArtiklID = 1, Naziv = 'A1', Cena = 10, VaziOd = '20110101'
                                    , STariDatum = NULL,  StaraCena = NULL, StariNaziv = NULL
;
SELECT * FROM PromeneImenaIcene
;
   ArtiklID Naziv                  Cena VaziOd                  STariDatum                          StaraCena StariNaziv
----------- ----- --------------------- ----------------------- ----------------------- --------------------- ----------
          1 A1                    10.00 2011-01-01 00:00:00.000 NULL                                     NULL NULL

(1 row(s) affected)

-- prva promena, 07 januara promenili smo ime iz A1 u A2 i cenu iz 10 u 11 dinara:
INSERT INTO PromeneImenaIcene (ArtiklID, Naziv, Cena, VaziOd, STariDatum,  StaraCena, StariNaziv)
                            SELECT ArtiklID = 1, Naziv = 'A2', Cena = 11, VaziOd = '20110107'
                                    , STariDatum = '20110101',  StaraCena = 10, StariNaziv = 'A1'
;
SELECT * FROM PromeneImenaIcene
;
   ArtiklID Naziv                  Cena VaziOd                  STariDatum                          StaraCena StariNaziv
----------- ----- --------------------- ----------------------- ----------------------- --------------------- ----------
          1 A1                    10.00 2011-01-01 00:00:00.000 NULL                                     NULL NULL
          1 A2                    11.00 2011-01-07 00:00:00.000 2011-01-01 00:00:00.000                 10.00 A1

(2 row(s) affected)


Pokusajmo da napravimo istu gresku zbog koje smo krtikovali Nadino resenje:
Code:
-- Pokuajmo da umetnemo novi red izmedju prva dva, recimo 3 Januara
INSERT INTO PromeneImenaIcene (ArtiklID, Naziv, Cena, VaziOd, STariDatum,  StaraCena, StariNaziv)
                            SELECT ArtiklID = 1, Naziv = 'A03', Cena = 13, VaziOd = '20110103'
                                    , STariDatum = '20110107',  StaraCena = 11.00, StariNaziv = 'A2'

-- ne moze:
Msg 547, Level 16, State 0, Line 1
The INSERT statement conflicted with the CHECK constraint "CK_StariDatum_Stariji_od_Novog". The conflict occurred in database "test", table "dbo.PromeneImenaIcene".
The statement has been terminated.

Sta ako nam stvarno treba novi red sa datumom 3 Januar, jer smo ga zaboravili pri unosu? Sve zavisi od realnosti. Ako nismo izadl;i ni jednu fakturu koja vuce cenu izmedju 1 i 7 Januara 2011, onda je OK. Obrisimo sve redove koji su uneti sa datumima vecim od 3 Januar. Brisanje je moguce samo jedan po jedan red, i to poslednji red,. Znaci da smo sprecili i nenamerna brisanja mase redova.

Ako smo pak izdali neku fakturu koja cita cenu iz bilo kog od unetih redova, onda treba spreciti brisanje tih redova. Tu nam sad treba funkcija ili triger, da spreci brisanje.

O nakakvom UPDATE nad istorijskom tabelom nemojte ni da sanjate.

Sve u svemu, brisanje je jako otezano, izmene su prakticno nemoguce - a to je princip na kome se poslednjih 200 godina i nesto duze zasniva knjigovodstvo. nema umetanja redova unazad, nema brisanja podataka i pisanja novih vrednosti. Ako je neka transakcija uenta pogresno, ispravka se vrsi dodavanjem nove transakcije koja ce stanje dovesti na tacnu vrednost.

Kad se knjigovodstvo vodi na papiru, princip nepromenljivosti unetih transakcija se lako obezbedjuje ispartanim papirom i mastiljavom olovkom. Kompjuteri nazalost imju UPDATE i DELETE komande i mi se uglavnom ne stitimo od toga. Istina, uglavnom je UDATE/DELETE par sakriven od korisnika, ali nije od programera. A programei su ti koji brljaju po bazi, nisu korisnici.

Gde ja radim, obracun plata, poreza, godisnjih odmora, bolovanja, prisustva na poslu je prebacen sa COBOLa na ORACLE. Divno, napredujem u dobrom pravcu. Tu se cuva izmedju ostalog i podatak o bracnom stanju i deci, zbog koordinacije zdravstvenog osiguranja. I broj dana godisnjeg odmora na raspolaganju. COBOL je bio spor i nezgraopan, ali je radilo sve kako treba. U ORACLE, iznenada smo svi postali razvedeni i godisnji odmor nam je nestao. Neko je odradio neki DELETE ili UPDATE a da nije primetio gresku. Greku je primecena kad je nekome odbijen zahtev za neplatu lekarskih usluga, za zenu i decu, jer u sistemu je ta osoba 'single', iako se vide deca i bracni drug. Sistem nije imao ogranicenje (CONSTRAINT) koji trazi da ako imas bracnog druga da ne mozes biti 'single'. I nije imao, niti ima, pracenje raspolozivog broja dana za godisnji odmor.

Zato je ZIdar ovoliko dosadan sa ogranicenjima, CONSTRAINTS pa CONSTRAINTS, check, FK ili PK, nema veze, samo da se zavede nekakav red u bazi.

 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo06.04.2011. u 08:23 - pre 141 meseci
Zidar u vezi prvog propusta tipa
Citat:

Code:
-- Slucaj 2: pogresan datum, raniji od dosada unetih
-- STa ako sada unesem neki potpuno pogresan datum, recimo 10 januar 2011?
INSERT INTO dbo.Status_HistoryES(Id, datumod ,StariStatus ,cena ,naziv)
SELECT ID = 1, datumod = '2011/01/10', StariStatus = 4, Cena = 104.00, Naziv = 'P4'
-- proslo je a ne bi trebalo da prodje!


posto je to bio moj prvi i osnovni constraint, pa sam morala proveriti kako je moguce, pustila sam tvoju scriptu i proverila kod mene, i u tvojoj scripti nema UDF fn_StariDatum, i zato prolazi pogresno unesen datum. Naravoucenije za mene, drugi put da pustim celosnu scriptu.

Citat:

ALTER TABLE [dbo].[Status_HistoryES] WITH CHECK
ADD CONSTRAINT [CK_DatumOdStatus_HistoryES]
CHECK (([dbo].[fn_StariDatumOD]([Id],[StatusId])<=[DatumOd])) -- izmena, bilo je 'fn_StariDatum'
GO


Tacno je da je u ovom constraintu bilo fn_StariDatum, i treba da bude da ne bi prosao pogresan datum..Ne znam zasto si menjao...fn_StariDatumOd mi je za computed kolonu StariDatumOd.
 
Odgovor na temu

nadavesela
programer, DZS

Član broj: 199298
Poruke: 93
195.26.131.*



+3 Profil

icon Re: Computed DatumDo06.04.2011. u 09:14 - pre 141 meseci
Posle uspesnog ogranicenja koje si imao sa
Code:

INSERT INTO PromeneImenaIcene (ArtiklID, Naziv, Cena, VaziOd, STariDatum,  StaraCena, StariNaziv)
                            SELECT ArtiklID = 1, Naziv = 'A03', Cena = 13, VaziOd = '20110103'
                                    , STariDatum = '20110107',  StaraCena = 11.00, StariNaziv = 'A2'


Testirala sam i ja malo ovo drugo resenje :) i nesumice unela
Code:

INSERT INTO PromeneImenaIcene (ArtiklID, Naziv, Cena, VaziOd, STariDatum,  StaraCena, StariNaziv)
                            SELECT ArtiklID = 1, Naziv = 'A03', Cena = 13, VaziOd = '20110103'
                                    , STariDatum = '20110101',  StaraCena = 11.00, StariNaziv = 'A2'

Ovo prolazi, i treba da prodje, ali zar ne treba StariDatum mesto '20110101' da bude '20110107' jer je to datum poslednjeg zapisa koji se menja.
Zakljucak, znaci treba da gadjam datum (isto kao sto treba da gadjam StariStatus) u mom resenju.
Mislim da istoriske izmene, podrazumevaju mogucnost izmene samo jednog reda za izabrani entitet, tako da sa kolonom Aktivan ili u mom resenju, koristeci StariDatumOd IS NULL, mogu istoriski evidentirati snizenje svih cena za dati iznos ili procent, a da pri tom imam istoriske izmene.
Code:

INSERT INTO dbo.Status_HistoryES (Id,DatumOd,StariStatus,cena,naziv)
SELECT Id,'20110119',StatusId as StariStatus,cena-20.00 as cena ,naziv from dbo.Status_HistoryES Where StariDatumOd is null



[Ovu poruku je menjao nadavesela dana 06.04.2011. u 10:30 GMT+1]
 
Odgovor na temu

Zidar
Canada

Član broj: 15387
Poruke: 3085
*.100.46-69.q9.net.



+79 Profil

icon Re: Computed DatumDo06.04.2011. u 14:27 - pre 141 meseci
Originalna skripta nije htela da prodje, pa sam promenio tako da moze da prodje. Zato pozeljno da se pripremi primer i testira sve, specijalno za clanak. OK, znaci moze da se napravi da radikako treba, a to je najvaznije.

Ako se porede resenja, imamo jedno gde se broj kolna duplira (moj predlog) i drugo gde originalna tebla ostaje ista, a ogranicenja se postizu kroz user-defined funkcije, ugrubo po jedna za svaku kolonu koja se prati. U razlicitim okolnostima, verovatno bi se resenja ponasala razlicto, u smislu brzine.

U mnogim knjigama sam nasao da funkcije u ulozi constraints mogu da uspore rad, ako ima mnogo redova. To nisam uspeo da vidim u praksi, iako u nekim situacijama debelo koristim funkcije na tabelama sa desetak miliona redova, i to povelikih redova. Isto tako, resenje po metodi Alexa Kuznetsova trebalo bi da uspori insert, jer se procesira dvostruko veci broj kolona i indeksi su ogromni. Ne mogu ni da potvrdim ni da odbacim, tamo gde sam primenio resenja Alexa Kuznetsova nisma jos dostigao toliko veliki broj redova da bi se usporenje osetilo. Metoda Kuznetsova implicitno podrazumeva da se unosi jedan po jedan red (mada ne mora) i tada je tesko dokazati da je nesto toliko sporo da bi se na front endu osetilo.

U svakom slucaju, Nadi sve cestitke za odlicno obavljen posao. Ono malo sto smo dobili kao ideju, da nekako treba obezbediti nepromenljivost unetih podataka, nepromenljivost istorije ako hocete, Nada je resila na originalan nacin. Nije lako da se razume, ako ni resenje Kuznetsova sto nije lako da se razume. Ali, kad jednom ukapirate, mnogi problemi koji su do sada bili ne resivi postaju resivi. naravno da ovo ne resava sve moguce probleme, ali otavar nam horizonte zasigurno.

 
Odgovor na temu

[es] :: MS SQL :: Computed DatumDo

Strane: 1 2 3

[ Pregleda: 6368 | Odgovora: 52 ] > FB > Twit

Postavi temu Odgovori

Navigacija
Lista poslednjih: 16, 32, 64, 128 poruka.