[ mika @ 22.08.2007. 20:26 ] @
Pozdrav svima,

Radim bazu u kojoj se vrsi evidencija dokumentacije. Korisnički zahtev je takav da je potrebna numeracija dokumenata na sledeći način: prva četiri broja jesu godina, druga dva su mesec a sledeća četiri jesu broj dokumenta - u tom mesecu. Obzirom da nije veliki broj dokumenata, sigurno ih mesečno biti više od 10.000 tako da je dovoljno četiri cifre za numeraciju.

Primer:

2007080001
2007080002
.
.
.
2007089999
2007090001
.
.
.
2007099999

Takođe, zahteva se da korisnik NE UNOSI sam numeraciju (tj. da ne pamti koji je poslednji broj) već da sistem sam da "predlog" koji bi to broj bio, ujedno i korišćen kao primarni ključ.

Prvih 6 cifara u numeraciji se lako dobijaju primenom SELECT FORMAT(DATE(), "YYYY") i SELECT FORMAT(DATE(), "MM")- (ovo je pseudo-SQL kod, ne zamerite mi, znate na šta mislim).

E sad, interesuje me kako da dođem do numeracije samog dokumenta?

Rešenje 1) za svaki INSERT treba da se odradi sledeci pseudo-SQL:

SELECT MAX(id) FROM tabela WHERE datum LIKE "2007-08"

... pa da tom broju dodam 1 i da onda CONCAT-ujem taj broj sa datumom i tako ga snimim u tabelu? Ovo rešenje mi se čini nezgrapnim, jer za svaki unos treba da se prođe kroz celu bazu da bi se pronašao sledeći broj, čini mi se da to i nije baš optimalno.

Rešenje 2) - da u nekoj sporednoj tabeli čuvam poslednji unos koji je sačuvan u bazi, i da ga prilikom svakog INSERT-a povećavam za 1? Ovo drugo rešenje mi se čini optimalnije, ali sam hteo da postujem ovde svoje mišljenje, pa da neko od vas prokomentariše.

Pozdrav, očekujem komentare, pro & contra, i ako neko ima neko drugo rešenje/predlog neka postuje! Unapred hvala!

[ chachka @ 22.08.2007. 21:21 ] @
Imamo procenu: broj dokumenata u mesecu <= 10000.
i grubo: 25 radnih dana u mesecu sa po 8 radnih sati.

Kada se sve to izdeli, dođe se do proseka od 50 dokumenata na sat, odnosno 1 dokument po minuti. I da se predpostavi da postoje periodi intenzivnijih unosa dokumenata od recimo 3 puta, dolazi se do toga da se unosi 1 dokument na svakih 20 sekundi. Oba tvoja rešenja su dovoljno brza za ovo.

Meni se lično više sviđa prvo rešenje jer je manje podložno greškama.
[ savkic @ 22.08.2007. 23:47 ] @
Ja bih ti savetovao malo drugačiji dizajn, pre svega taj broj dokumenta nikako nemoj uzeti za primarni ključ. Koristi neki običan INTEGER koji korisnik neće ni videti.
Polje broj indeksiraj tako da ćeš MAX vrednost dobijati brzo. Ako sam dobro razumeo, program treba da predloži naredni broj ali je korisnik slobodan da unese drugi? Ako je tako, možeš očekivati da će korisnici uneti nešto drugo, da neće poštovati formatiranje itd.
[ mika @ 23.08.2007. 07:42 ] @
@Chacka: 10x na komentaru! Može li još kojim metodom da se ovo reši, a da bude optimalnije, ako pretpostavimo da ima, npr. 1.000.000 dokumenata mesečno ?

@Savkic: Da, rekoh da program treba da predloži broj dokumenta koji ce biti unet - posle sam shvatio da ako predloži, da samim time program sam i usvoji taj broj - to polje u kome će biti predlozen broj bice disabled. Razmišljao sam da i pored ovog ključa imam obican auto_increment koji je nevidljiv za korisnika, ali obzirom da cu indeksirati po ovom numeracijskom polju, i indeks ce biti UNIQUE - onda nema potrebe, po meni, za dodatnim primarnim ključem.

Hvala, ima li jos nekih mišljenja?

Pozz :)

[ broker @ 23.08.2007. 08:37 ] @
Razmotri usput dva problema:

a) vise korisnika istovremeno unosi dokumente. Moze da se desi da dva ili vise unosa u nekom mmentu dobiju izgenerisan isti broj dokumenta.

b) pored unosa, dokumenti mogu i da budu obrisani. Ako je poslednji dokument obrisan, onda ce sledeci dokuiment koji se unosi dobiti njegov broj. To moze da bude potencijalni problem.

U ovakvim dvoumljenjima, ja se po pravilu opredeljujem za autoincrement kao primarni kljuc. Datum dokumenta sigurno u tabeli postoji kao posebno polje, tako da je ponavljanje u kljucu cista redundansa.

Porazgovaraj s aklijentom dali taj zahtev o formatu sifre ima neki konkretan smisao ili je to samo nekome dunulo da je "tako lepse".
[ CandyMan @ 23.08.2007. 09:12 ] @
Zdravo!

Moj predlog je da koristiš polja broj i mesec, gde bi mesec bio definisan u sporednoj tabeli kao npr. 200708 imao početni i krajnji datum (01.08.2007. i 31.08.2007.) - ko zna zbog čega je ovo dobro. Ova dva polja bi činila složeni ključ i eksplicitno bi imao numeraciju koja u svakom mesecu počinje od 1. Broj bi se dodeljivao sa
Code:

select nvl(max(broj),0)+1
  from tabela
 where mesec = 200708

Funkcija nvl važi za Oracle, druge baze imaju nz, isnull... i služi da se hendluje slučaj kada nema niti jednog dokumenta u datom mesecu tj. kada max(broj) vrati null.
Verovatno je negde u igri i neko datumsko polje koje bi moglo da se iskoristi u kombinaciji sa autoincrementom, ali o tome možemo i kasnije.

Ovo rešenje je dovoljno brzo za većinu realnih sistema. Kod mene u firmi, u tabeli koja ima od 5 do 8 hiljada zapisa mesečno koristimo ovo rešenje. Chachkina matematika plastično objašnjava da, ako negde može da se arči vreme to je insert podataka tj. šta god misliš da odradiš sa podacima - uradi to dok se unose.

Samo kratak osvrt na zadatak: ako je korisnik tražio nekakvu numeraciju, to ne mora da znači da tako treba da izgleda primarni ključ.
--- Edit posle pročitane brokerove poruke---
Select max ima smisla da se izvrši tek kada korisnik pritisne taster za snimanje podataka i kada se odradi sva validacija podataka, nikako ranije, da bi izbegli da dva korisnika dobiju isti broj. I u ovom slučaju postoji teorijska mogućnost da se desi greška, ali to nisam imao priliku da vidim, još uvek... :)

[Ovu poruku je menjao CandyMan dana 23.08.2007. u 10:22 GMT+1]
[ mika @ 23.08.2007. 09:31 ] @
@Broker: 10x za info, o mogućnošću dobijanja istog broja za dva vremenski bliska unosa nisam ni razmišljao, a to je BAŠ realan slučaj...


Što se tiče zahteva klijenta, on hoće da na osnovu broja dokumenta čovek odmah zna u kom je mesecu/godini taj dokument - da bi mogao da ga fizički nađe u registratoru :) radi se o dokumentaciji koja se mora čuvati u hardcopy-ju pa eto... zato je čovek tako hteo. Kad mu neko npr. faxom pošalje: "... na osnovu vašeg dokumenta br. 200702-0003 dogovorili smo... ", onda on automatski zna gde da ga nađe, što ima neke logike.

Brisanje dokumenta - ne verujem da ću tu funkciju implementirati, možda implementiram storniranje - jedan BOOLEAN u record-u promenim sa 0 na 1 i onda je dokument read-only.

10x još jednom, mi se znamo još iz perioda BBS-ova, pre nekih 10-ak i više godina...


@CandyMan: Ovo mi se čini interesantnim - da U TOKU INSERT-a izgenerišem broj dokumenta - dva konkurentna INSERTA nisu moguća istovremeno, pogotovo ako se radi u transakciji - transakcija počne sa LOCK TABLE, zatim se izgeneriše broj, odradi se INSERT, pa zatim UNLOCK table. Šta mislite o ovakvom rešenju?

Pozz
[ chachka @ 23.08.2007. 09:42 ] @
SELECT MAX bi trebalo odraditi u BEFORE INSERT trigeru nesto poput pseudo koda
Code:

broj := SELECT MAX ...;
LOOP
  TRY
    INSERT broj ...
    EXIT LOOP;
  EXCEPT
    WHEN DUPLICATE_KEY THEN
      broj := broj + 1;
  END;
END;

Ako ne prodje INSERT zbog duplog ključa onda ga triger sam povećava za jedan i ponovo pokušava dok ne prođe.


Drugo, vektorski ključevi (ključevi sa potpunim ili delimičnim značenjem) zaista više nemaju smisla uz moderne metode pretraživanja.
Ali... ljudi vole vektorske ključeve!
Zaista mi je lakše da mi neko kaže: 'Daj mi dokument broj 37 od 25-og Aprila!', nego 'Daj mi dokument broj 265409!'. Prva varijanta mi je lakša za pamćenje i manje podložna grešci. Verovatno ću i posle minut pamtiti koji dokument treba da dostavim. Koliko sekundi ću pamtiti onaj šestocifreni broj? 5 do 10, čim mi skrenu misli s njega moraću da tražim da mi se ponovi broj.
[ mika @ 23.08.2007. 09:47 ] @
Ok, znači trigger koristimo u takvom slučaju.


Citat:
Drugo, vektorski ključevi (ključevi sa potpunim ili delimičnim značenjem) zaista više nemaju smisla uz moderne metode pretraživanja.
Ali... ljudi vole vektorske ključeve!
Zaista mi je lakše da mi neko kaže: 'Daj mi dokument broj 37 od 25-og Aprila!', nego 'Daj mi dokument broj 265409!'. Prva varijanta mi je lakša za pamćenje i manje podložna grešci. Verovatno ću i posle minut pamtiti koji dokument treba da dostavim. Koliko sekundi ću pamtiti onaj šestocifreni broj? 5 do 10, čim mi skrenu misli s njega moraću da trađim da mi se ponovi broj.


Upravo zbog toga i razumem čoveka koji mi je dao zahtev za baš takvim ključem... BTW, nisam znao da se u teoriji baza podataka takva vrsta ključa zove vektorskim, 10x za informaciju!
[ chachka @ 23.08.2007. 10:08 ] @
Što se tiče milion dokumenata u jednom mesecu:

Milion dokumenata fizički ne može da unese jedan operater. Znači imamo više operatera, odnosno više radnih stanica za unos dokumenata.

Ja bih tada primeni high-low sistem dodeljivanja šifara.

Recimo da se i dalje insistira na vektorskom broju dokumenta. Preuredio bih ga u format:

gggg-mm-ooo-bbbbbb

g = godina
m = mesec
o = operater ili radna stanica
b = redni broj u okviru godina, meseca i operatera.

Kako se ovo koristi? Operater se loguje na sistem. U tom momentu se zna gggg-mm-ooo deo novog broja dokumenta. Od baze se traži MAX broj GROUP BY godina, mesec, sifra_operatera. To je broj bbbbbb. Program sam povećava ovaj broj za jedan pri svakom upisu novog dokumenta u bazu. Kad se operater diskonektuje ceo proces ide ispocetka - login, traženje MAX-a i cepaj!

Ovim se dodela broja dokumenta raspodelila na više radnih stanica, pa je server rasterećen.

Sa high-low sistemom sam se prvi put susreo čitajući radove Scott Ambler-a, a konkretno Choosing a Primary Key: Natural or Surrogate?. Mada je za mene većina Amblerovog pisanja po pitanju baza besmislena, ovo je metod koji sam itekako usvojio.
[ broker @ 23.08.2007. 10:15 ] @
Citat:
mika
Što se tiče zahteva klijenta, on hoće da na osnovu broja dokumenta čovek odmah zna u kom je mesecu/godini taj dokument - da bi mogao da ga fizički nađe u registratoru :) radi se o dokumentaciji koja se mora čuvati u hardcopy-ju pa eto... zato je čovek tako hteo. Kad mu neko npr. faxom pošalje: "... na osnovu vašeg dokumenta br. 200702-0003 dogovorili smo... ", onda on automatski zna gde da ga nađe, što ima neke logike.


To je sasvim razuman zahtev ali uopste ne mora da bude sproveden kao kljuc. Naime, taj broj mozes da generises i prikazujes cak i prilikom prikazivanja/stampanja dokumenta. Gledaj na to kao na drugaciji format prikaza datuma i broja dokumenta i nista vise od toga.
[ mika @ 23.08.2007. 10:30 ] @
Citat:
To je sasvim razuman zahtev ali uopste ne mora da bude sproveden kao kljuc. Naime, taj broj mozes da generises i prikazujes cak i prilikom prikazivanja/stampanja dokumenta. Gledaj na to kao na drugaciji format prikaza datuma i broja dokumenta i nista vise od toga.



Ok je to, medjutim - potrebno je da se svakog meseca krene sa numeracijom od početka. Znači od 1.9. dokumenti opet kreću sa 0001, 0002 itd... So, ne mogu da stavim običan ključ formatiran sa datumom kao br. dokumenta, već bih morao da koristim rutine koje smo razmatrali ranije u tekstu...