29.10.07

Kako pronaci poslednji AUTO_INCREMENT?

Pitanje je kako pronaci poslednji AUTO_INCREMENT?

Odgovor je funkcija LAST_INSERT_ID() koja vraca broj poslednjeg auto_inc koji je uradjen u bazi.
S obzirom da funkcija vraca vrednost koja se odnosi samo na postojeceg klijenta eto resenja za nasa prebacivanja tj. necemo morati da radimo select max(id)....
Za opsirnije o ovoj funkciji pogledajte :
http://dev.mysql.com/doc/refman/5.0/en/stored-procedure-last-insert-id.html


www.baco.co.yu

18.10.07

Lista MySQL Server Error Codes

Na adresi
http://dev.mysql.com/doc/refman/5.0/en/error-messages-server.html
imate kompletan spisak za gresaka sa kodovima istih, trebace vam za hendlovanje...

Povratne informacije sa servera (procedure, funkcije,triggeri)

Pošto težište bacamo na serverski deo što se tiče razvoja, postujem jedan članak o tome kako možete sa server da dobijete neku povratnu informaciju da li je nešto prošlo kako treba ili nije.
Konkretno, dajem dva mala primera da vidite kako nesto može da se realizuje u tom pravcu:

Prvi primer:
Varijable sa kojom će te se susretati na serverskoj strani su one sa prefiksom @. To su varijable globalnog tipa na nivou sesije (ovaj koncept je MySql maznuo iz ORACLE-a). Ako imate neku varijablu koji bilo gde postavite na neku vrednost, u toku cele sesije je možete pozivati i koristiti tu vrednost. ako se na isti nalog (npr. root) loguje više korisnika istovremeno, svako će imati svoju kopiju iste varijable - ovo možete i da proverite. Znači, ako Vam za bilo šta zatreba ovo onda je korsitite, korisna je stvarčica. Evo i primera:






Naravno ako bilo kada u toku iste sesije pozovete datu promenljivu, dobićete njenu upisanu vrednost. Ovo je dobro da možete da pri obradama generisete poruke sa servera i prikazujete ih na klijentu. Ovo ima upotrebu ako želite da na nivou sesije čuvate neke podatke (kada je pristupio korisnik sistemu, šta je radio, neke druge poruke,...)


Drugi primer:
IN/OUT/INOUT varijable - na server strani MySql-a možete da kreirate proceduru u kojoj imate jedan ili više IZLAZNIH parametara. Evo primer kako rade izlazni parametri:


/*AKO POSTOJI BRISEMO PROCEDURU*/
DROP PROCEDURE if EXISTS OUT_DEMO;
/*KREIRAMO PROCEDURU*/
CREATE PROCEDURE out_demo(IN p_ulaz int,OUT p_izlaz int)
BEGIN
DECLARE i int DEFAULT 0 ;
SET p_izlaz := p_ulaz*2;
END;
/*ZOVEMO PROCEDURU DA VIDIMO KAKO TO SVE RADI*/
CALL OUT_DEMO(1,@S);
/*PROVERAVAMO REZULTAT*/
SELECT @S;



www.baco.co.yu

17.10.07

Problemi sa dužinom podataka pri importu sa drugim izvora

Prebacivao sam u našu shemu podatke iz drugih baza (Oracle, XML,DBF, Access...) i jedna od stvari koja može pri migraciji podataka da stvara probleme - ovde najviše mislim na presipanje podataka za FinSyM - to će biti neminovnost, je memorija koju zauzimaju naši karakteri mogu da naprave mnogo problema (dešava se da nije uvek 1 znak 1 byte). Postoji više načina čuvanja naših karaktera, pa da predstavim moguće situacije:
  1. Podaci se cuvaju u YUCII karakteru (š,č,ć,ž,đ) - sa ovim nema problem sve bude kako treba, jedan karakter je jedan char u bazi
  2. Podaci se čuvaju u ASCII karakteru (^,[,],#...) - sa ovim takođe nema problema, jedan karakter je jedan char u bazi. Jedina akcija nakon upucavanja koja treba da se odradi je da se realizuje UPDATE NEKA_TABELA SET NEKO_POLJE=REPLACE(NEKO_POLJE,'^','Č') i onda COMMIT i stvar je rešena.
  3. Podaci se cuvaju kao KUKE i KVAKE (ae,....,/\) - sa ovim zna da bude problema, jer na primer: imamo proizvod koji se zove PŠENICA, to u bazi zauzima 7 char-ova dok PaeENICA zauzima preko 20 karaktera. Ovo jako pravi problem jer ce sa baze generisati greska kako je prekoračena maksmalna dužina polja (npr. VACHAR(7)). Onda treba posvetiti pažnju ovakvim problemima i anticipirati ovakav vid izuzetaka u radu.


www.baco.co.yu

15.10.07

Ogranicenja obracuna

Zbog prosirenja funkcionalnosti postoji sledece ogranicenje u procesu obracunavanja:
Nije moguce koristiti formulu na elementu obracuna koji ce se pojavljivati i na ponderisanim i na pojedinacnim analizama. Razlog je taj sto se u formuli mora navesti i tabela koja se gleda. Resenje koje treba implementirati je neka vrsta parsera koji ce formulu srediti u real-time modu, dinamicki neposredno pre izvrsavanja...
Ovo je od interesa samo za kukuruz, jer do sada je sve ostalo proslo bez problema.

Labels:

12.10.07

Primer za obradu na serveru pomocu uskladištenih procedura

Dajem jedan skolski primer (nije nalickan do kraja ali je koncept regularan i tako cemo raditi) koji radi i koji prebacuje profakturu u fakturum, upisuje jedan slog u zaglavlje fakture na osnovu podataka iz profakture i onoiko slogova stavki koliko ima u stavkama profakture.
ovo radi nad shemom aragon-a, pa proanalizirajte kako se to radi jer cemo SVE OBRADE raditi na ovaj nacin kroz procedure.

Naravno, sada nisam imao vremena da definisem i EXCEPTION HANDLER-e i slicno ali kada napravimo sve za produkciju to ce izgledati mnogo detaljnije.

SRC CODE:


/*brisemo proceduru ako postoji*/
DROP PROCEDURE IF EXISTS p_profak_fak;

/* kreiramo proceduru p_profak_fak
* ulazni parametri su broj (ID) profakture,
* i sifra radnika koji radi obradu*/
CREATE PROCEDURE p_profak_fak(IN p_brojprofakture int,IN p_sifrad int)
COMMENT 'PREBACIVANJE PROFAKTURA => FAKTURA'
BEGIN

/*upisujemo u zaglavlje fakture */
INSERT
INTO f_fakzag
(dat_izd,
parid,
status_dok,
parime,
datknj,
genbroj,
tip,
sifrad,
brspoljdo,
nacnas,
datum_valute,
datum_prometa)

/*na osnovu SELECT-a odabrane profakture*/
SELECT(SELECT now()
FROM dual) /*tekuce vreme*/,parid /*sifra partnera je ista*/,
status_dok /*
dokument je OK */
,parime /*naziv partnera je isti prepisuje se*/,NULL /*datum
knjizenja je NULL-faktura nije proknjizena*/
,NULL /*GENBROJ je NULL unece se triggerom*/,'U' /*ULAZNA FAKTURA*/
,p_sifrad,id /*ovo je broj profakture SPOLJNI DOKUMENT*/,'P' /*
faktura je nastala od profakture*/
,datum_valute /*prepisuje se*/,datum_prometa

/*prepisuje se*/
FROM f_profakzag

/* BIRAMO SAMO JEDNU PROFAKTURU
* SA WHERE ID USLOVOM*/
WHERE id = p_brojprofakture;

/* potvrdimo transakciju za zaglavlje
* zbog ID-fakture da uensemo u stavke*/
COMMIT;

/*UNOSIMO STAVKE FAKTURE*/
INSERT
INTO f_faktab
(id,
zagid,
sifrob,
opis,
kol,
cena,
pdvproc,
vrednost,
parbatproc,
ukupno)

/*NA OSNOVU SELECT-a iz stavki profakture*/
SELECT id,(SELECT max(id)
FROM f_fakzag) /*zaglavlje se trazi upitom*/,sifrob,opis,kol,cena
,pdvproc,vrednost,rabatproc,ukupno
FROM f_profaktab pft
WHERE pft.zagid = p_brojprofakture;

/*potvrdimo citavu transakciju*/
COMMIT;
END



PROCEDURU POZIVATE SA:

call p_profak_fak(1,1);

www.baco.co.yu

SQL - napredne tehnike Knowledge Expert


Jedna od korisnih stvari (alata) za učenje SQL-a za MySql server je i KnowledgeExpert u okviru alata Toad for MySql (www.quest.com/toad-for-mysql/). Alat TOAD for MySql je bestplatan, pa ga slobodno koristite.

Knowledge eXpert je ugrađena baza znanja u vezi sa SQL-om, ne samo sintaksom, nego i sa puno prime
ra iz domena transakcija, procedura, sigurnosti i održavanja baze. Jako bi bilo korisno da instalirate Toad u verziji 3 i da prostudirate šta sve može da se nađe u Knowledge eXpertu.
Moje iskustvo sa TOAD-om i Knowledge eXpertom za ORACLE baze je više nego odlično, pa nam ostaje da probamo isto to sa MySql-om

Knowledge expret ima standardni meni za pretragu i prikaz strukture dokumentacije (index,search, topic,locate...) pa je lako pronaći ono što Vam treba.





www.baco.co.yu

4.10.07

SQL - korisne stvari za učenje kreiranje uskladištene procedure

Preuzeto sa http://www.outsourcing-it.com


Novije verzije MySQL-a podrzavaju izmedju ostalog i uskladistene procedure (stored procedures), pa bih zelio prikazati na jednom primjeru, kako se one kreiraju i koriste.

Za primjer cemo uzeti jednu novcanu transakciju, odnosno prebacivanje novca sa racuna jednog korisnika na racun drugog korisnika. Kreirajmo dvije jednostavne tabele ‘korisnici‘ i ‘transakcije‘, u kojima cemo drzati podatke o korisnicima i obavljenim transakcijama:

CREATE TABLE `testDB`.`korisnici` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`ime` VARCHAR(45) NOT NULL DEFAULT '',
`iznos` DOUBLE(10,2),
`prezime` VARCHAR(45) NOT NULL DEFAULT '',
`email` VARCHAR(100),
PRIMARY KEY(`id`),
UNIQUE INDEX (`email`)
)
ENGINE = InnoDB
CHARACTER SET utf8
COLLATE utf8_general_ci
COMMENT = 'Test tabela za uskladistenu proceduru';

CREATE TABLE `testDB`.`transakcije` (
`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
`ovome_oduzmi` INTEGER UNSIGNED,
`ovome_dodaj` INTEGER UNSIGNED,
`iznos` DOUBLE(10,2),
`opis_transakcije` VARCHAR(255),
PRIMARY KEY(`id`),
CONSTRAINT `FK_transakcije_oduzmi`
FOREIGN KEY `FK_transakcije_oduzmi` (`ovome_oduzmi`)
REFERENCES `korisnici` (`id`)
ON DELETE SET NULL,
CONSTRAINT `FK_transakcije_dodaj`
FOREIGN KEY `FK_transakcije_dodaj` (`ovome_dodaj`)
REFERENCES `korisnici` (`id`)
ON DELETE SET NULL
)
ENGINE = InnoDB
CHARACTER SET utf8
COLLATE utf8_general_ci
COMMENT = 'Lookup tabela sa transakcijama';

Napomena: Da biste koristili ACID transakcije u MySQL-u, morate imati aktiviran InnoDB engine.

Nakon sto smo kreirali te dvije tabele, popunicemo ih nekim testnim podacima, a za to cemo upotrebiti jednu uskladistenu proceduru ‘popuni_tabele‘ (Download: izvorni kôd ; Download: screenshot). Kreirajte tu proceduru koristeci prilozeni izvorni kôd i pozovite ju sa odgovarajucim parametrima - prvi parametar oznacava broj korisnika, koje zelite kreirati, a u drugom parametru ce se nalaziti izlazna poruka procedure:

mysql> call popuni_tabele(30, @poruka);
+----------------------------------------------------------------------------+
| p_poruka |
+----------------------------------------------------------------------------+
| Broj unesenih korisnika: 30; Broj obavljenih transakcija: 870 |
+----------------------------------------------------------------------------+
Query OK, 0 rows affected (0.29 sec)

mysql>SELECT @poruka;
+----------------------------------------------------------------------------+
| @poruka |
+----------------------------------------------------------------------------+
| Broj unesenih korisnika: 30; Broj obavljenih transakcija: 870 |
+----------------------------------------------------------------------------+
1 row in set (0.00 sec)

Sada bi se u tabelama trebalo nalaziti dovoljno podataka za testiranje.

Za obavljanje jedne novcane transakcije, kreiracemo proceduru ‘obavi_transakciju

(izvorni kod je na dnu teksta ), koja prihvata slijedece parametre:
- p_oduzmi_id (broj korisnika, kojemu se skida odredjeni iznos sa racuna),
- p_dodaj_id (broj korisnika, kojemu se dodaje odredjeni iznos na racun),
- p_iznos (iznos, koji ce se jednom korisniku skinuti sa racuna, a drugom dodati),
- p_opis (opis transakcije) i
- p_poruka (izlazna poruka procedure).

Najprije cemo prikazati rezultat nekoliko testova, a onda slijedi objasnjenje kompletne procedure.

Test 1.
Sa racuna korisnika 1 potrebno je prebaciti 500 EUR na racun korisnika 2.

Pogledajmo prvo trenutno stanje racuna oba korisnika:

mysql> SELECT * FROM korisnici WHERE id IN(1, 2);
+----+--------+----------+------------+--------------------+
| id | ime | iznos | prezime | email |
+----+--------+----------+------------+--------------------+
| 1 | ime_1 | 8813.18 | prezime_1 | email1@domena1.com |
| 2 | ime_2 | 10384.60 | prezime_2 | email2@domena2.com |
+----+--------+----------+------------+--------------------+

Pozovimo proceduru ‘obavi_transakciju‘:

mysql> CALL obavi_transakciju(1, 2, 500.00, 'Prebaci 500 EUR
-> sa racuna korisnika 1 na racun korisnika 2', @poruka);
+--------------------------------------------+
| p_poruka |
+--------------------------------------------+
| SUCCESS! Transakcija je uspjesno obavljena |
+--------------------------------------------+
1 row in set (0.05 sec)
Query OK, 0 rows affected (0.06 sec)

Provjerimo opet trenutno stanje oba korisnika nakon obavljene transakcije:

mysql> SELECT * FROM korisnici WHERE id IN (1, 2);
+----+-------+----------+-----------+--------------------+
| id | ime | iznos | prezime | email |
+----+-------+----------+-----------+--------------------+
| 1 | ime_1 | 8313.18 | prezime_1 | email1@domena1.com |
| 2 | ime_2 | 10884.60 | prezime_2 | email2@domena2.com |
+----+-------+----------+-----------+--------------------+
2 rows in set (0.00 sec)

Iznos kod prvog korisnika se smanjio za 500, a kod drugog se povecao za 500. Provjerimo i u tabeli ‘transakcije’, da li je transakcija uspjesno obavljena:

mysql> SELECT * FROM transakcije ORDER BY id DESC LIMIT 1\G
*************************** 1. row ***************************
id: 875
ovome_oduzmi: 1
ovome_dodaj: 2
iznos: 500.00
opis_transakcije: Prebaci 500 EUR sa racuna korisnika 1 na racun korisnika 2

1 row in set (0.00 sec)

Dakle, test je uspjesno obavljen.

Test 2.
Sa racuna korisnika 1 prebacimo na racun korisnika 2 veci iznos, nego sto korisnik 1 posjeduje. Transakcija ne smije biti obavljena.

Trenutno stanje je isto kao nakon obavljene transakcije u prvom testu. Pozovimo proceduru ‘obavi_transakciju‘:

mysql> CALL obavi_transakciju(1, 2, 9999.99, 'Sa racuna korisnika 1 prebaci
-> na racun korisnika 2 veci iznos nego sto korisnik 1 posjeduje', @poruka);
+-----------------------------+
| p_poruka |
+-----------------------------+
| Korisnik nema dovoljno love |
+-----------------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)

Provjerimo trenutno stanje:

mysql> SELECT * FROM korisnici WHERE id IN (1, 2);
+----+-------+----------+-----------+--------------------+
| id | ime | iznos | prezime | email |
+----+-------+----------+-----------+--------------------+
| 1 | ime_1 | 8313.18 | prezime_1 | email1@domena1.com |
| 2 | ime_2 | 10884.60 | prezime_2 | email2@domena2.com |
+----+-------+----------+-----------+--------------------+
2 rows in set (0.00 sec)

Kao sto vidite, stanje je nepromijenjeno. Provjeravanjem tabele ‘transakcije‘, uvjericemo se da transakcija nije obavljena.

Test 3.
Pokusajmo prebaciti 500 EUR sa racuna nepostojeceg korisnika na racun korisnika 2. Transakcija nece biti obavljena i pojavice se greska o nepostojecem korisniku.

Pozovimo proceduru ‘obavi_transakciju‘:

mysql> CALL obavi_transakciju(31, 2, 500.00, 'Prebaci 500 EUR sa
-> racuna korisnika 31 na racun korisnika 2', @poruka);
+---------------------------------------------+
| p_poruka |
+---------------------------------------------+
| Korisnik nije pronadjen pod zadanim brojem! |
+---------------------------------------------+
1 row in set (0.00 sec)
Query OK, 0 rows affected (0.01 sec)

Provjeravanjem trenutnog stanja i liste obavljenih transakcija, uvjericemo se da transakcija nije obavljena, jer zadani korisnik ne postoji.


Linija 1: Posto se u MySQL-u znak ‘;‘ koristi za terminaciju nekog izraza, odnosno kao ‘end-of-query‘ terminator, u uskladistenim procedurama i funkcijama moramo koristiti neki drugi terminator (delimiter). Drugi delimiter postavljamo pomocu naredbe DELIMITER, a u ovoj proceduri smo kao delimiter uzeli $$.

Linija 3: MySQL trenutno ne podrzava izraz ‘CREATE OR REPLACE …” pomocu kojeg bismo jednostavno mogli kreirati novu proceduru ili zamijeniti vec postojecu, pa zato moramo najprije ukloniti postojecu proceduru.

Linije 5-9: Naredbom ‘CREATE PROCEDURE‘ kreiramo zeljenu proceduru sa odgovarajucim parametrima. Parametri mogu biti IN (ulazni), OUT (izlazni) i INOUT (ulazno-izlazni). U ovom slucaju imamo 4 ulazna i 1 izlazni parametar.

Linija 10: Naredbom BEGIN zapocinjemo izvrsni dio procedure (routine body), odnosno blok (compund statement) u kojem se izvrsavaju odredjeni izrazi.

Linija 11: Vrsimo deklaraciju varijable v_trenutno_stanje, u koju cemo spremati trenutni iznos racuna od zadanog korisnika.

Linija 13: Naredbom BEGIN zapocinjemo unutrasnji (ugnijezdjeni, nested) blok izraza.

Linija 14: Vrsimo deklaraciju handler-a (btw. moze li mi neko smisleno prevesti ovu rijec handler?) za odredjeni uslov/uzrok. Kada dodje do zadanog uzroka, handler izvrsava zadane naredbe. Trenutno postoje 2 vrste handler-a: CONTINUE i EXIT, a osim njih, tu je jos i handler UNDO, koji ce biti podrzan u nekoj od iducih verzija MySQL-a.
Ako za odredjeni uslov definisemo CONTINUE handler, onda ce izvrsavanje bloka, u kojem se taj handler nalazi, biti nastavljeno bez prekida.
Ako za odredjeni uslov definisemo EXIT handler, onda ce izvrsavanje bloka, u kojem se taj handler nalazi, biti prekinuto.
Ako za odredjeni uslov definisemo UNDO handler, onda ce izvrsavanje bloka, u kojem se taj handler nalazi, biti prekinuto i sve izmjene uzrokovane tom procedurom ce biti ponistene (ROLLBACK).

Uslov (condition) moze biti naveden kao oznaka neke MySQL greske i to u obliku kao:
- SQLSTATE vrijednost
- predefinisana oznaka za neke od generalnih uslova: NOT FOUND, SQLWARNING ili SQLEXCEPTION
- rucno (proizvoljno) deklarisan uslov, koji je vezan za odredjeno stanje (SQLSTATE) ili neku MySQL gresku

SQLWARNING obuhvata sve SQLSTATE kôdove, koji zapocinju sa 01.
NOT FOUND obuhvata sve SQLSTATE kôdove, koji zapocinju sa 02.
SQLEXCEPTION obuhvata sve SQLSTATE kôdove, koji nisu obuhvaceni sa SQLWARNING ili NOT FOUND.

U nasem slucaju smo za uslov NOT FOUND definisali handler EXIT. To znaci, da ce uslov NOT FOUND biti ispunjen, ukoliko SELECT naredba u proceduri (linije 28-31) ne pronadje zadanog korisnika. Posto taj uslov bude ispunjen, handler EXIT ce biti automatski pozvan, pri cemu ce u izlaznu varijablu p_poruka spremiti odgovarajuci tekst i nakon toga prekinuti daljnje izvrsavanje tog bloka.

Linije 17-21: Vrsimo deklaraciju handler-a za sve uslove, koji nisu prethodno obuhvaceni. Ukoliko dodje do neke greske, ponistavaju se sve izmjene nad podacima (ROLLBACK) i smjestamo odgovarajuci tekst u izlaznu varijablu p_poruka.

Linija 23: U izvornom kodu mozemo ubacivati proizvoljne komentare. Komentar zapocinje sa duplom crticom.

Linija 24: Naredbom ‘START TRANSACTION‘ oznacavamo pocetak transakcije.

Linije 28-31: Ovim SELECT upitom provjeravamo da li zadani korisnik postoji (p_oduzmi_id) i koliko love ima trenutno na racunu. Ukoliko zadani korisnik nije pronadjen, odnosno ukoliko ne postoji, bice uzrokovan uslov NOT FOUND i handler EXIT ce prekinuti daljnje izvrsavanje ovog bloka.

Linije 34-48: Ukoliko zadani korisnik postoji i ukoliko na racunu trenutno ima veci iznos od iznosa, koji mu se mora skinuti, onda mu se zadani iznos (p_iznos) skida sa racuna i prebacuje na racun drugog zadanog korisnika (p_dodaj_id). Nakon sto to prebacivanje novca izvrsi, sacuvacemo informacije o toj obavljenoj transakciji.
Ukoliko zadani korisnik postoji, ali nema dovoljno love na racunu, prebacivanje novca nece biti obavljeno i u izlaznu poruku ce biti ubacen tekst “Korisnik nema dovoljno love“.

Linija 49: Naredbom END oznacavamo kraj unutrasnjeg (nested) bloka.

Linije 53-58: Provjeravamo da li je izlazna varijabla prazna ili nije.
Ukoliko jeste, onda je sve u redu i mozemo da potvrdimo uspjesno obavljenu transakciju sa naredbom COMMIT, pri cemu u izlaznu varijablu ubacujemo tekst o uspjesno obavljenoj transakciji.
Ukoliko nije, ponistavaju se sve izmjene sa naredbom ROLLBACK.

Linija 60: Opcionalno mozete izlistati sadrzaj varijable p_poruka.

Linija 61: Naredbom END oznacavamo kraj glavnog bloka. Delimiterom $$ izvrsavamo prethodno napisanu proceduru.

Linija 63: Vracamo delimiter na originalnu prvobitnu vrijednost ‘;‘.

Procedura je spremna za koristenje.

Zasto koristiti uskladistene procedure?
Mnogo zadataka se moze obaviti i izvan nekog RDBMS-a, bez obzira koji programski jezik ili platformu koristili. Medjutim, neke zadatke je daleko lakse i brze izvesti direktno u RDBMS-u, cime ce aplikacija biti rasterecena i efikasnija u obavljanju preostalih zadataka.

Pretpostavimo da morate ovaj zadatak (novcana transakcija) obaviti pomocu PHP-a i MySQL-a. Ne smijem ni zamisliti koliko izvornog kôda biste morali napisati u PHP-u i koliko zahtjeva biste morali proslijediti MySQL-u da bi obavio sve naredbe potrebne za uspjesno obavljanje ove transakcije.

Uz pomoc ove uskladistene procedure, iz PHP-a je potrebno samo pozvati ovu proceduru i proslijediti odgovarajuce parametre.

Osim toga, zamislite da morate obaviti mnogo operacija nad vise tabela (sumiranje, grupisanje, sortiranje i sl.) - zar se ne namece samo po sebi rjesenje sa uskladistenim rutinama (procedurama i funkcijama)?


IZVORNI KOD:
DELIMITER $$

DROP PROCEDURE IF EXISTS `testDB`.`popuni_tabele` $$
CREATE PROCEDURE `testDB`.`popuni_tabele` (IN p_broj_korisnika INT, OUT p_poruka VARCHAR(255))
LANGUAGE SQL
NOT DETERMINISTIC
MODIFIES SQL DATA
SQL SECURITY DEFINER
COMMENT 'Ova procedura sluzi za popunjavanje tabela ''korisnici'' i ''transakcije'' '
BEGIN
-- deklaracija potrebnih varijabli
DECLARE v_counter INT DEFAULT 0;
DECLARE v_broj_transakcija INT DEFAULT 0;
DECLARE v_oduzmi_id INT;
DECLARE v_dodaj_id INT;
DECLARE v_iznos DOUBLE(10,2);
DECLARE nema_vise_redova BOOLEAN DEFAULT false;

DECLARE cur_korisnici CURSOR FOR
SELECT k1.id AS oduzmi, k2.id AS dodaj
FROM korisnici k1, korisnici k2
WHERE k1.id <> k2.id;

DECLARE continue HANDLER FOR SQLSTATE '02000' SET nema_vise_redova = true;
DECLARE exit HANDLER FOR SQLEXCEPTION
BEGIN
ROLLBACK;
SET p_poruka = 'Unos prekinut zbog SQLExceptiona';
END;

-- oznacimo pocetak transakcije
START TRANSACTION;

-- petlja_za_unos_podataka
WHILE v_counter <>
DO

-- povecaj brojac za 1
SET v_counter = v_counter + 1;

INSERT INTO korisnici (id, ime, prezime, iznos, email)
VALUES ( NULL,
CONCAT('ime_', v_counter),
CONCAT('prezime_', v_counter),
RAND() * (10/RAND()) * 1000,
CONCAT('email', MOD(v_counter, 5), '@domena', MOD(v_counter, 7), '.com')
);

END WHILE;

-- otvori cursor sa korisnicima
OPEN cur_korisnici;

-- otvori petlju sa korisnicima
petlja_sa_korisnicima: LOOP
-- pokupi vrijednosti iz cursora
FETCH cur_korisnici INTO v_oduzmi_id, v_dodaj_id;

-- ako cursor ne sadrzi vise redova, zavrsi sa petljom
IF nema_vise_redova THEN
LEAVE petlja_sa_korisnicima;
END IF;

-- povecaj brojac transakcija za 1
SET v_broj_transakcija = v_broj_transakcija + 1;

SET v_iznos = RAND() * (MOD(v_broj_transakcija, 33) + 10);

-- necemo vrsiti provjeru koliko je trenutni korisnikov iznos,
-- nego cemo samo unijesti podatke radi demonstracije funkcionisanja:
UPDATE korisnici
SET iznos = iznos - v_iznos
WHERE id = v_oduzmi_id;

UPDATE korisnici
SET iznos = iznos + v_iznos
WHERE id = v_dodaj_id;

-- unesi transakciju u tabelu
INSERT INTO transakcije(id, ovome_oduzmi, ovome_dodaj, iznos, opis_transakcije)
VALUES (NULL, v_oduzmi_id, v_dodaj_id, v_iznos, CONCAT('Transakcija broj ', v_broj_transakcija));


-- zatvori petlju sa korisnicima
END LOOP petlja_sa_korisnicima;

CLOSE cur_korisnici;

-- potvrdimo unos i oznacimo kao kraj transakcije
COMMIT;

-- sastavimo OUT poruku
SET p_poruka = CONCAT('Broj unesenih korisnika: ', v_counter, '; Broj obavljenih transakcija: ', v_broj_transakcija);

-- prikazimo sadrzaj poruke:
SELECT p_poruka;

END $$

DELIMITER ;


Z.Z.Baćo Razvojni tim

SQL - korisne stvari za učenje

Pstujem Vam linkove za literaturu, tutorijale i kvizove za SQL (uglavnom sam se trudio sa to bude u duhu MySql servera, ali ima svega, uglavnom sve je po ANSII standardu).
  1. http://dev.mysql.com/doc/refman/5.0/en/tutorial.html"
    - glavna stvar (po meni). Oficijelni tutorajal za MySQL
  2. http://www.w3schools.com/sql/default.asp - kviz za SQL jezik (opste reference)
  3. http://sql-info.de/mysql/gotchas.html- tips and tricks, malo viši nivo, ali je veoma korsitan link
  4. http://www.geocities.com/SiliconValley/Vista/2207/sql1.html - kviz
  5. http://www.sql-tutorial.net/ - tutorijal

Z.Z.Baćo Razvojni tim

3.10.07

MySql INSERT -> SELECT statement

Ovo će biti jako korisno za obrade, molim Vas da
detaljno proučite kako ovo funkcioniše.
Poenta je da kreirate insert statement
kao neki select iz druge/iste tabele.
Koristan link je:
http://dev.mysql.com/doc/refman/5.0/en/
tamo se nalazi kompletna MYSQL dokumentacija.

radovan

INSERT [LOW_PRIORITY | HIGH_PRIORITY] [IGNORE]
[INTO] tbl_name [(col_name,...)]
SELECT ...
[ ON DUPLICATE KEY UPDATE col_name=expr, ... ]

With INSERT ... SELECT, you can quickly insert many rows into a table from one or many tables. For example:

INSERT INTO tbl_temp2 (fld_id)
SELECT tbl_temp1.fld_order_id
FROM tbl_temp1 WHERE tbl_temp1.fld_order_id > 100;

The following conditions hold for a INSERT ... SELECT statements:

  • Specify IGNORE to ignore rows that would cause duplicate-key violations.

  • DELAYED is ignored with INSERT ... SELECT.

  • The target table of the INSERT statement may appear in the FROM clause of the SELECT part of the query. (This was not possible in some older versions of MySQL.) In this case, MySQL creates a temporary table to hold the rows from the SELECT and then inserts those rows into the target table. However, it remains true that you cannot use INSERT INTO t ... SELECT ... FROM t when t is a TEMPORARY table, because TEMPORARY tables cannot be referred to twice in the same statement

  • AUTO_INCREMENT columns work as usual.

  • To ensure that the binary log can be used to re-create the original tables, MySQL does not allow concurrent inserts for INSERT ... SELECT statements.

  • Currently, you cannot insert into a table and select from the same table in a subquery.

  • To avoid ambigious column reference problems when the SELECT and the INSERT refer to the same table, provide a unique alias for each table used in the SELECT part, and qualify column names in that part with the appropriate alias.

In the values part of ON DUPLICATE KEY UPDATE, you can refer to columns in other tables, as long as you do not use GROUP BY in the SELECT part. One side effect is that you must qualify non-unique column names in the values part.



www.baco.co.yu

2.10.07

Formule za kukuruz

Samo sirovi kukuruz ima neke nebulozne stvari za unos i obracun a to su:

Prva formula od interesa je za vlagu:
(1-((100-labmertabunos)/86))

Ukoliko treba da prvo odbijes neki staticni trosak, recimo 3% onda to glasi:
(97/100)*(1-((100-labmertabunos)/86))

Za neki drugi element recimo primese koje imaju neki staticni odbitak pre je:
(97/100)*((labmertabvred-labmertabunos)/100)


Sve ostale varijante se mahom mogu progurati bez formula...

Mogli ste da primetite da je formula ustvari izraz koji ce biti pomnozen sa kolicinom da bi se dobila konkretna vrednost korekcije. Zamislite to ovako "select formula_izraz*kolicina".
polja labmertabunos i labmertabvred su polja iz tabele labmertab, oona se mogu zameniti poljima labuslugeunos i labuslugevred, odnosno labisplateunos i labisplatevred respektivno, sve u zavisnosti od samog elementa da li je P,J ili I...

  1. U labmertab tabelu idu samo elementi 'P' koji su 'Aktiv' tj. cekirano je "da li se element prikazuje u laboratoriji"
  2. U labUslugetab tabelu idu elementi koji su 'J' ili ('P' i 'Pasiv') i oni zajedno cine usluge
  3. U labIsplatetab tabelu idu elementi koji imaji vrednost nacina skidanja 'I'
Ako nesto iskrsne, ovaj post ce biti dopunjen.

www.baco.co.yu

Labels:

1.10.07

Kompleksniji DBGrid

Pošto će nam za FinSyM trebati (u više navrata) Dbgrid koji u sebi ima CheckBox, evo primera kako sve to funkcioniše. Postoje 2 aritkla: prvi je opštiji za upoznavanje, drugi je konkretan primer.

Ovo je recimo jako zgodno za izbor nekih dokumenata za knjiženje, arhiviranje storniranje i slično. Ovo nam olakšava situaciju kada korisnik iz nekog grida sa dosta stavki, može da izabere one koje želi i da posle uradi nešto da njima.

Prvi artikal:
The power of the DBGrid
I've said this many times, but I'll say it again: Delphi's DBGrid ... what a powerful component! Contrary to most other Delphi data-aware controls, the DBGrid component has many nice features and is more powerful than you would have thought.

The "standard" DBGrid does its job of displaying and manipulating records from a dataset in a tabular grid. However, there are many ways (and reasons) why you should consider customizing the output of a DBGrid.
Here's an example:
Suppose you have a boolean field in your dataset. By default, the DBGrid displays boolean fields as "True" or "False" depending on the value of the data field. If you think the same way I do, it is much more visually attractive to be able to you use a "true" check box control to enable editing of such fields.

This article serves as an entry point to a series of articles describing how to place just about any component into a cell of a DBGrid.

Placing controls in DBGrid
As some of you might not know, in-place editing of a DBGrid cell's contents is accomplished via a "small" edit control that is displayed over the cell. Inside DBGrid, there is a TInplaceEdit that moves around the grid - the Edit component that you enter your data into. The rest of the unfocused cells are really just "pictures".

But a CheckBox, a ComboBox, ...?
So what? If there is a floating Edit over the DBGrid, we'll float any control we like. There are no new ideas here, in fact, the basic technique simply mimics what the DBGrid does internally. What you will learn here, is how to float any type of visual control around the DBGrid.

A sample database
Following the concepts set out in the Beginners Guide to Delphi Database Programming, examples below use ADO components (AdoQuery/AdoTable connected to ADOConnection, DBGrid connected to AdoQuery over DataSource) to display the records from a database table in a DBGrid component. All the component names were left as Delphi named them when dropped on the form (DBGrid1, ADOQuery1, AdoTable1, ...)
If you do not know how to display records from a database table (or query) in a DBGrid component, please explore the "Connecting to a database" chapter.

Here's a sample MS Access database we'll use to explore the topic of adding components to a DBGrid. The QuickiesContest.MDB databse has three tables: Subjects, Authors and Articles. The database was originally created to hold the entries to our Delphi Programming Quickies Contest.
The picture below displays the relationships between tables. For the moment note that the Winner field in the Articles table is a YesNo field (boolean).

Delphi Quickies Contest database
Download database
Components in a DBGrid - the Theory
When adding controls to a DBGrid, there are a couple of steps and questions to be revealed. Here's what has to be done to place a DBCheckBox inside a DBGrid cell, and enable editing of a boolean field using the CheckBox component:
  1. A DBCheckBox needs to be placed (invisible) on a Form and linked to a boolean field in the DataSource component that supplies the DBGrid with a dataset.
  2. If the cell holding a boolean field is focused the CheckBox component should be made visible and placed in the appropriate cell. When no longer needed the CheckBox needs to be made invisible again.
  3. If the cell holding a boolean field is NOT focused a sample graphics should be drawn on the cell indicating whether a field's value is True or False.
  4. When in editing mode, all keystrokes are going to the DBGrid's cell; we have to make sure they are sent to the CheckBox. Note that we are primarily interested in the [Tab] and the [Space] key. "Tab" should move the input focus to the next cell, and [Space] should toggle the state of the CheckBox.
Note: the above "steps" are more or less the same no matter what component is placed in (i.e. floating over) the cell.
Components in a DBGrid - Theory into Practice
Finally, here's how all the above theory looks in practice (with more examples coming in the near future):

Placing a CheckBox in a DBGrid
Here's how to place a check box into a DBGrid. Create visually more attractive user interfaces for editing boolean fields inside a DBGrid.
DBCheckBox in DBGrid

Drop down pick list inside a DBGrid - part 1
Here's how to place a drop down pick list into a DBGrid. Create visually more attractive user interfaces for editing lookup fields inside a DBGrid - using the PickList property of a DBGrid column.

Lookup field with PickList

Drop down list (DBLookupComboBox) inside a DBGrid - part 2
Here's how to place a DBLookupComboBox into a DBGrid. Create visually more attractive user interfaces for editing lookup fields inside a DBGrid - place a DBLookupComboBox into a cell of a DBGrid.

DBLookupComboBox in a DBGrid

DateTimePicker inside a DBGrid
Here's how to place a TDateTimePicker into a DBGrid. Create visually more attractive user interfaces for editing date/time fields inside a DBGrid - place a drop down calendar into a cell of a DBGrid.

DateTimePicker in a DBGrid

Drugi artikal:

This is the first article, in the series of articles named "Adding components to a DBGrid". The idea is to show how to place just about any Delphi control (visual component) into a cell of a DGBrid. If you are unfamiliar with the idea, please first read the "Adding components to a DBGrid" article.
CheckBox in a DBGrid?
As discussed in the above article, there are many ways (and reasons) why you should consider customizing the output of a DBGrid: suppose you have a boolean field in your dataset. By default, the DBGrid displays boolean fields as "True" or "False" depending on the value of the data field. If you think the same way I do, it is much more visually attractive to be able to you use a "true" check box control to enable editing of such fields.
Creating a sample application
To begin, start Delphi and, on that default empty new form, place a TDBGrid, a TADOTable, and a TADOConnection, TDataSource. Leave all the component names as Delphi named them when dropped on the form (DBGrid1, ADOQuery1, AdoTable1, ...). Use the Object Inspector to set a ConnectionString property of the ADOConnection1 (TADOConnection) component to point to the sample QuickiesContest.mdb MS Access database. Connect DBGrid1 to DataSource1, DataSource1 to ADOTable1, and finally ADOTable1 to ADOConnection1. ADOTable1's TableName property should point to the Articles table (thus making the DBGrid display the records of the Articles table).

If you have set all the properties correctly, when you run the application (given that the Active property of the ADOTable1 component is True) you should see the following output:

Boolean fields in a DBGrid

What you need to "see" in the above picture is that, by default, the DBGrid displays the boolean field's value as "True" or "False" depending on the value of the data field. The field that holds the boolean value is "Winner".

What we are up against in this article is to make the above picture look like the one below:

DBCheckBox in DBGrid
CheckBox in a DBGrid!
Or, better to say, a DBCheckBox in a DBGrid.

Ok, here we go. To show a check box inside a cell of a DBGrid we'll need to make one available for us at run time. Select the "Data controls" page on the Component Palette and pick a TDBCheckbox. Drop one anywhere on the form - it doesn't matter where, since most of the time it will be invisible or floating over the grid. TDBCheckBox is a data-aware control that allows the user to select or deselect a single value - most appropriate for boolean fields.

Next, set its Visible property to False. Change the Color property of DBCheckBox1 to the same color as the DBGrid (so it blends in with the DBGrid) and remove the Caption. And most importantly, make sure the DBCheckBox1 is connected to the DataSource1 and to the correct field (DataSource = DataSource1, DataField = Winner).

Note that all the above DBCheckBox1's property values can be set in the form's OnCreate event like:

procedure TForm1.FormCreate(Sender: TObject);
begin
DBCheckBox1.DataSource := DataSource1;
DBCheckBox1.DataField := 'Winner';
DBCheckBox1.Visible := False;
DBCheckBox1.Color := DBGrid1.Color;
DBCheckBox1.Caption := '';

//explained later in the article
DBCheckBox1.ValueChecked := 'Yes a Winner!';
DBCheckBox1.ValueUnChecked := 'Not this time.';
end;

What comes next is the most interesting part. While editing the boolean field in the DBGrid, we need to make sure the DBCheckBox1 is placed above ("floating") the cell in the DBGrid displaying the boolean field. For the rest of the (non-focused) cells carrying the boolean fields (in the "Winner" column), we need to provide some graphical representation of the boolean value (True/False). This means you need at least two images for drawing: one for the checked (True value) state, and one for the unchecked (False value) state. The easiest way to accomplish this is to use the Windows API DrawFrameControl function to draw directly on the DBGrid's canvas.

Here's the code in the DBGrid's OnDrawColumnCell event handler that occurs when the grid needs to paint a cell.

procedure TForm1.DBGrid1DrawColumnCell(
Sender: TObject; const Rect: TRect; DataCol:
Integer; Column: TColumn; State: TGridDrawState);

const IsChecked : array[Boolean] of Integer =
(DFCS_BUTTONCHECK, DFCS_BUTTONCHECK or DFCS_CHECKED);
var
DrawState: Integer;
DrawRect: TRect;
begin
if (gdFocused in State) then
begin
if (Column.Field.FieldName = DBCheckBox1.DataField)
then
begin
DBCheckBox1.Left := Rect.Left + DBGrid1.Left + 2;
DBCheckBox1.Top := Rect.Top + DBGrid1.top + 2;
DBCheckBox1.Width := Rect.Right - Rect.Left;
DBCheckBox1.Height := Rect.Bottom - Rect.Top;

DBCheckBox1.Visible := True;
end
end
else
begin
if (Column.Field.FieldName = DBCheckBox1.DataField)
then
begin
DrawRect:=Rect;
InflateRect(DrawRect,-1,-1);

DrawState := ISChecked[Column.Field.AsBoolean];

DBGrid1.Canvas.FillRect(Rect);
DrawFrameControl(DBGrid1.Canvas.Handle, DrawRect,
DFC_BUTTON, DrawState);
end;
end;
end;

To finish this step, we need to make sure DBCheckBox1 is invisible when we leave the cell:

procedure TForm1.DBGrid1ColExit(Sender: TObject);
begin
if DBGrid1.SelectedField.FieldName =
DBCheckBox1.DataField then
DBCheckBox1.Visible := False
end;

We need just two more events to handle.
Note that when in editing mode, all keystrokes are going to the DBGrid's cell, we have to make sure they are sent to the CheckBox. In the case of a CheckBox we are primarily interested in the [Tab] and the [Space] key. [Tab] should move the input focus to the next cell, and [Space] should toggle the state of the CheckBox.

procedure TForm1.DBGrid1KeyPress
Sender: TObject; var Key: Char);
begin
if (key = Chr(9)) then Exit;

if (DBGrid1.SelectedField.FieldName
= DBCheckBox1.DataField)
then
begin
DBCheckBox1.SetFocus;
SendMessage
(DBCheckBox1.Handle, WM_Char, word(Key), 0);
end;
end;

And finally, the last touch. It could be appropriate for the Caption of the checkbox to change as the user checks or unchecks the box. Note that the DBCheckBox has two properties (ValueChecked and ValueUnChecked) used to specify the field value represented by the check box when it is checked / unchecked. My ValueChecked property holds 'Yes a Winner!' and ValueUnChecked equals 'Not this time.'

procedure TForm1.DBCheckBox1Click(Sender: TObject);
begin
if DBCheckBox1.Checked then
DBCheckBox1.Caption := DBCheckBox1.ValueChecked
else
DBCheckBox1.Caption := DBCheckBox1.ValueUnChecked;
end;

That's it. Run the project and voila ... check boxes all over the Winner field's column.

www.baco.co.yu

Korisni linkovi

Prikaz linkova koji mogu biti od koristi:

Delphi linkovi:
  • www.about.com - tips and tricks varijante za Delphi
  • www.elitesecurity.org - najbolji domaći forum, bogat infromacijama vezanim za Delphi
  • www.delphibasics.co.uk - sajt za onsovne informacije
  • www.torry.ru - kompletan Delphi sajt za gomilom informacija, članaka, primera i komponenti
MySql linkovi:
  • www.mysql.com - osnovna stranica za MySql

Oracle linkovi:
  • www.oracle.com/technology/software/products/database/xe/index.html - skidanje Oracle XE verzije baze (full Oracle besplatna baza)
  • www.oracle.com/tehnology - sajt za kompletnim pregledima Oracle produkata
  • tahiti.oracle.com - skriveni server sa najboljom literaturom za Oracle produkte
  • otn.oracle.com - Oracle Technology Network - zvaničan Oracle forum

www.baco.co.yu

Nova stvar kod nas

Rešio sam da malo unapredimo neke stvari kod nas u poslu. Sve stvari što se tiču nekih novosti oko tehnologija, naših aplikativnih rešenja, knjiga, rešavanja problema ću kačiti ovde tako da budu svima dostupni.

Ovde ćemo lakše čuvati stvari i dolaziti do informacija i rešenja koje smo nekada razjasnili.

radovan

www.baco.co.yu