Oracle stoji iza kupnje MySQL-a?

February 4, 2008 – Dejan

The Sun-MySQL deal stinks” - kaže John Dvorak i dodaje:”Uvjeren sam da Oracle želi kupiti MySQL sa ciljem da ga ubije, ali nije mogao sam povući okidač, pa je poslao marionetu da obavi posao.

Ta tvrdnja je idealna koska za sve one, koji su gladni teorija zavjere. Neko će kosku zagristi, neko će ju ignorisati, a neko će samo usputno komentarisati - kao ja npr.

Ja lično ne vjerujem, da Oracle stoji iza te kupovine, ali vrijeme će pokazati kakvi konci su se vukli u vezi ove akvizicije.

S druge strane, kakvu korist ima Sun od toga? Sun ima širok spektar hardware i software proizvoda, a najpoznatiji je po OS Solarisu, SPARC serverima i programskom jeziku Javi.

Javi dakle. Hmmmm … Da pokušam analizirati to malkice…

MySQL se uglavnom asocira uz PHP. PHP je trenutno najpopularniji programski jezik za web. Web je IT-budućnost opstanka svih IT firmi. Takoreći svi sajtovi, koji su urađeni u PHP-u, koriste MySQL kao backend database engine. Sa sigurnošću se može reći da MySQL daje PHP-u ogroman boost u popularnosti i rasprostranjenosti na web tržištu. Što direktno znači manji dio kolača za Javu.

I sad tu uskače Sun kao sinonim za Java programski jezik, sa ciljem da razvoj i budućnost MySQL-a usmjeri po svojim željama, dakle ka boljoj i dubljoj simbiozi Jave i MySQL-a, a na štetu PHP-a. Na stranu to što PHP sve više liči na Javu.

Znači, Sun i Oracle bi iz ovoga trebali izaći kao dobitnici, a MySQL, PHP i naivna OpenSource zajednica kao pušioničari.

Ja se nadam da Sun neće “ubiti” MySQL i da MySQL neće nestati sa scene u idućih 2-3 godine, jer nestanak konkurencije ne donosi ništa pozitivno. Ionako imam već gorka iskustva sa Oracleom, a kako li bi tek bilo, da nedostatak konkurencije dozvoli Oracleu da se prema tržištu i svojim mušterijama odnosi monopolistički - što uključuje bahatost, paprene cijene, loš support i td. 

Kao i uvijek do sada - vrijeme će pokazati šta se krije iza ove kupovine…


Common Table Expressions i iterativni ciklusi

January 21, 2008 – Zidar

Upotreba CTE i iterativni procesi

Kako bismo u MS SQL ispisali prvih 50 celih brojeva tako da svaki broj bude u posebnom redu?
1
2
3

50

Nešto ovako bi možda pomoglo:

DECLARE @i int
SET @i = 0
WHILE @i<=50
BEGIN
 SET @I = @i + 1
 SELECT @i AS i
 CONTINUE
END

Nažalost, svaki SELECT se izvrsava za sebe:

          i
-----------
          1
(1 row(s) affected)
          i
-----------
          2
(1 row(s) affected)
          i
-----------
          3

Ovo je vec bolje:

DECLARE @i int
SET @i = 0
WHILE @i<=50
BEGIN
 SET @I = @i + 1
-- SELECT @i AS i
 PRINT @i
 CONTINUE
END

Međutim, s tim ne možemo ništa korisno da uradimo. Možda da rezultat cut/paste u Excel pa to da importujemo u SQL bazu, pa onda uradimo.. itd… itd….  Isuviše veliki posao za tako prostu stvar.

Na sreću, MS SQL 2005 ima Common Table Expressions. Probajte ovo:

WITH MyCTE AS
( 
SELECT 1 AS i
 UNION ALL
SELECT i+1
FROM MyCte
WHERE i<=50
)
SELECT * FROM MyCTE

Posto je ovo CTE, imamo posla u stvari sa temp tabelom i rezultat se moze upotrebiti u nekom SQL izrazu na način na koji smo pokazali u jednom od prethodnih napisa.

Pre nego objasnimo kako ovo radi i šta se dešava, evo još jednog prostog primera. Zadatak je ispisati sve dane u zadatom mesecu. Mogu da probam ovako:

WITH Mesec AS -- za mesec Mart 2008
(
SELECT CAST('2008/03/01' AS datetime) AS Dan
 UNION ALL
SELECT Dateadd(day,1,Dan ) AS Dan
FROM Mesec
WHERE Dan <= CAST('2008/03/30' AS datetime)
)
SELECT * FROM Mesec

Možemo da uopštimo problem i da tražimo da se vrate svi datumi između dva zadata datuma (datumski opseg, ‘date range’) Ovako nekako bi moglo:

DECLARE @OdDatuma as datetime, @DoDatuma as datetime
SET @OdDatuma = '2007/11/1'
SET @DoDAtuma = '2007/11/30'
 ; WITH OpsegDatuma AS -- ako WITH nije prva linija u batchu, stavlja se tacka zarez ispred
 (
 SELECT @OdDatuma AS Datum
  UNION ALL
 SELECT Dateadd(day,1,Datum) AS Datum
 FROM OpsegDatuma
 WHERE Dateadd(day,1,Datum) <= @DoDatuma
 )
 SELECT  * FROM OpsegDatuma

A možemo da napičemo i funkciju, OpesgDatuma, koja za zadata dva datuma vraća sve dane izmedju njih.

CREATE FUNCTION dbo.OpsegDatuma(@OdDatuma as datetime, @DoDatuma as datetime)
RETURNS TABLE
AS
RETURN
(
 WITH OpsegDatuma AS
 (
 SELECT @OdDatuma AS Datum
  UNION ALL
 SELECT Dateadd(day,1,Datum) AS Datum
 FROM OpsegDatuma
 WHERE Dateadd(day,1,Datum) <= @DoDatuma
 )
 SELECT Datum FROM OpsegDatuma
)

Sada može da se napiše i ovo:

SELECT * FROM dbo.OpsegDatuma('2007/11/1','2007/11/30')

ali i ovo:

SELECT
 D.Datum
 , COALESCE(COUNT(N.NarudzbaID),0) AS [Broj narudzbi na ovaj dan]
FROM dbo.OpsegDatuma('2007/11/1','2007/11/30') AS D
LEFT JOIN Narudzbe  AS N
ON D.Datum = F.DatumNarudzbe
GROUP BY D.Datum

Poslednji izraz vraća SVE datume u zadatom opsegu, pa i one kad nije bilo narudžbi.
Bez funkcije i bez Common Table Expression ovakav izraz bi bio veoma zahtevan za pisanje.

Vratimo se na pocčtak, da pokušamo da objasnimo šta se dešava. Izraz

WITH MyCTE AS
( 
SELECT 1 AS i
 UNION ALL
SELECT i+1
FROM MyCte
WHERE i<=50
)
SELECT * FROM MyCTE

ima 3 dela, kao i svaki CTE.

WITH MyCTE AS

jeste deklaracija CTE izraza, gde je MyCTE ime koje smo dodelili.

Izraz u zagradi

( 
SELECT 1 AS i
 UNION ALL
SELECT i+1 AS i
FROM MyCte
WHERE i<=50
)

je definicija naše temp tabele, šta će CTE da nam vrati.

Na kraju izrazom

SELECT * FROM MyCTE

pozivamo CTE.  Ništa novo u odnosu na ono što smo već ranije znali.

Izraz u zagradi je ono što nas interesuje. I tu imamo 3 dela:

SELECT 1 AS i

zove se ‘anchor member’, u slobodnom prevodu ‘prvi red CTE tabele’

UNION ALL

je deo sintakse i jednostavno tako mora  da se pise, tako trazi Microsoft
Treći deo je glavna stvar. Tu se dešavaju itaracije.

SELECT i+1
FROM MyCTE
WHERE i<=50

Zapazite FROM MyCTE. Izraz u zagradi je dakle unija dva seta, prvi deo vraća jedan red, a drugi iskaz poziva samu CTE tabelu tabelu. Otud rekurzija. Kao u matematici, funkcija poziva samu sebe.

Šta se dešava u pozadini? Anchor member vraća tačno jedan red. U tom momentu CTE ima taj jedan red. To se unionizuje sa izrazom

SELECT i+1 AS i
FROM MyCTE

što vracć još jedan red, sa uvećanim i za 1, koliko smo stavili u SELECT i+1
Sada već imamo dva reda u tabeli, vrednsti su 1,2. Ciklus se nastavlja dok važi WHERE uslov.
Posto imamo i=2, to je manje od 50, ciklus se nastavlja dok ne stignemo do 50. Ne pitajte me zašto ni kako, ali tako to radi.

Mnogo detaljnije i lepše objasnjenje imate ovde:
http://www.simple-talk.com/sql/sql-server-2005/sql-server-2005-common-table-expressions/
I zapamtite ime Nigel Rivett. Ot tog gospodina se ima mnogo sta naučiti.

Jos neke važne stvari da na pomenem. WHERE nije obavezno i u tom slucčju će, teorijski, ciklus biti beskonačan. Pošto beskonačnost postoji samo u teoriji, Microsoft je to ograničio na 100. Ako dakle odradite

WITH MyCTE AS
( 
SELECT 1 AS i
 UNION ALL
SELECT i+1
FROM MyCte
)
SELECT * FROM MyCTE

dobićete 101 red. Kakav bi to alat bio da se ne može primeniti na više od 100 ciklusa?

Probajmo

WITH MyCTE AS
( 
SELECT 1 AS i
 UNION ALL
SELECT i+1
FROM MyCte
WHERE i<=1000
)
SELECT * FROM MyCTE

i dobićemo grešku:

Msg 530, Level 16, State 1, Line 1
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.

Microsoft je mislio i na to. Postoji klauzula option (maxrecursion N) koja se dodaje na kraj svega, da bi se povecao broj iteracija, ako treba do vrednosti N.

WITH MyCTE AS
( 
SELECT 1 AS i
 UNION ALL
SELECT i+1
FROM MyCte
WHERE i<=1000
)
SELECT * FROM MyCTE
option (maxrecursion 5000)

I ovde ima ograničenje. N u option (maxrecursion N) može da ide do 32767.
Svakako da bi i ovo u praksi bilo malo. Zasto samo 32767? Samo Bog zna, a posto Boga nema, onda ne zna niko (Ovu rečenicu izgovorio je Zoran Radmilovic kao Radovan III, ja nemam ništa stim i ne odgovaram na moguće komentare)
Postoji nedokumentovana opcija  (ili bar slabo dokumentovana) (maxrecursion 0)

WITH MyCTE AS
( 
SELECT 1 AS i
 UNION ALL
SELECT i+1
FROM MyCte
WHERE i<=50000
)
SELECT * FROM MyCTE
option (maxrecursion 0)

Ako ste probali da odradite poslednjih nekoliko primera, uočite kakvom brzinom se proizvede recimo 50,000 brojeva.
Postoje egzotični nacčni da se isti rezultat dobije jos brzž, ali meni je i manje od 1 sec za 50,000 redova dovoljno brzo.
Svi ostali načini kojih se obični smrtnici mogu setiti su za dva reda velicine sporiji od WITH sa rekurzijom.
Ako želite nešto brze od ovoga, pogledajte knjigu ‘Inside SQL Server 2005: T-SQL Querying’, autori Itzik Ben-Gan, Lubor Kollar i Dejan Sarka. Treći autor je očigledno naš covek, a i Lubor nije daleko, tek da se zna :-)

Igrajte se neko vreme sa slicnim primerima, i svakako posetite sajt na kome piše Nigel Rivett. Kad svarimo ove ‘jednostavne’ upoterbe CTE izraza, pokušaću da se pozabavim hijerarhijama i mrežama.

 Ono što smo videli do sada čini mi se definitivno ubija potrebu za kursorima.
 


Delfini lete ka suncu - Sun kupio MySQL

January 17, 2008 – Dejan

MySQL maskota

Jeste li ikad vidjeli da delfini lete ka suncu?

U današnje vrijeme je sve moguće! Sun je kupio MySQL AB za milijardu dolara i najavljuje proboj na enterprise tržište.

Više informacija možete pročitati na blogu Jonathana Schwartza, CEO Sun-a.


Čudna mi čuda - CHECK constraint

January 16, 2008 – Zidar

Pre neki dan dobio sam zadatak da kreiram jednu prilično flat (nenormalizovanu) tabelu i da obezbedim integritet podataka upotrebom CHECK constraints. Pošto ja u stvari nisam DBA, je ovo u praksi retko radim, mislim, da zaista kreiram tabele i slično. Moja priča se završi obično sa ER dijagramom, posle normalizacije. Ali eto, zalomilo se. Na svoj užas i javnu sramotu, otkrio sam da ja to u stvari više ne ume da uradim. Odem u Help, tamo nema baš mnogo pomoći. Većina mojih knjiga je o logičkom modelitranju i normalizaciji i o specijalno zakukuljenim kverijima. Takoda sam morao da eksperimetišem. Mnogo mi se dopalo, pa sam poželeo da podelim iskustvo. Ako se nekom učini da je ovaj članak suviše trivijalan i jednostavan, unapred se izvinjavam.

Znam od ranije da se constraint može definisati pri definiciji kolone, a i posle. Zapitao sam se  šta je bolje, definisati CHECK prilikom definicije kolone, ili nakon kreiranja tabele, komandom ALTER table.

Izabrao sam prvi način, jer sam pretpostavio da ima manje kucanja. I krenuo sam ovako:

CREATE TABLE Test(ProjectId int NOT NULL CHECK(ProjectId = 51))

Onda sam pokušao da unesem pogrešan podatak, da testiram CHECK. Evo šta se desilo:

INSERT INTO Test VALUES (52)

Server me je pozdravio porukom:

The INSERT statement conflicted with the CHECK constraint “CK__Test__ProjectId__4222D4EF”. The conflict occurred in database “ScrapBook”, table “dbo.Test”, column ‘ProjectId’. The statement has been terminated.

Lepo, CHECK radi. Jedino šta mi se ne dopada je što me je na prvi pogled ime CK__Test__ProjectId__4222D4EF bilo malo nečitljivo. Sad vidim da CK znaši CHECK Test je ime tabele a ProjectID je ime kolone.  Meni je sve to promekolo i pažnju mi je odvuklo ono 4222D4EF . Interesantno akko najmanje važan podatak upada u oči. Onda sam probao drugi način, sa ALTER TABLE ADD CONSTRAINT i desilo se ovo:

DROP TABLE TestGO 

CREATE TABLE Test(ProjectId int NOT NULL )  

ALTER TABLE Test ADD CONSTRAINT ProjectId_must_be_51 CHECK(ProjectId = 51) 

Pokušaj da unesem 52 umesto zahtevanih 51 dao je ovakvu poruku:

Msg 547, Level 16, State 0, Line 1 The INSERT statement conflicted with the CHECK constraint “ProjectId_must_be_51″. The conflict occurred in database “ScrapBook”, table “dbo.Test”, column ‘ProjectId’.The statement has been terminated.

 Interesantno, odmah sam video o čemu se radi  - ProjectID mora  da bude 51.

Onda sam “pronašao” i treći način da sagradim jednostavni CHECK  - u definiciji kolone, sa zadavenjem imena.

DROP TABLE TestGO 

CREATE TABLE Test 

(ProjectId int NOT NULL CONSTRAINT ProjectId_must_be_51 CHECK(ProjectId = 51)  

, ProjectName varchar(50) NULL) 

Pokušaj da unesem šta ne sme da se unese:

INSERT INTO Test (ProjectID) VALUES (52)
Msg 547, Level 16, State 0, Line 1 The INSERT statement conflicted with the CHECK constraint “ProjectId_must_be_51″. The conflict occurred in database “ScrapBook”, table “dbo.Test”, column ‘ProjectId’.The statement has been terminated.

I još jedan način. Sada se CHECK definiše na nivou tabele, posle svih unetih kolona. Ovako:

CREATE TABLE Test

(ProjectId int NOT NULL 

, ProjectName varchar(50) NULL
, CONSTRAINT ProjectId_must_be_51 CHECK(ProjectId = 51)
)

Uočite zarez (zapetu) ispred reči CONSTRAINT. Tim zarezom  smo odvojili definiciju kolone ProjectName od definicije CHECK CONSTRAINT za kolonu ProjectID. Ovo će se kasnije pokazati kao jako korisan način.

Eto dakle četiri načina da se postavi jednostavan CHECK za individualnu kolonu.  Pa birajte koji vam se dopada. Za proste slučajeve verovatno je svejedno.

 Moj zadatak je bio teži, treblo je postaviti ne baš jednostavne CHECK koji su nad više od jedne kolone. Naravno, u propisno normalizovanoj tabeli po pravilu ne bi trebalo da imamo kolone koje zavise od drugih kolona u istoj tabeli, ali kako rekoh na početku, radi se o nenormalizovanoj tabeli.

U mojoj tabeli bile su dve povezane kolone, PkgType, integer i PackageCode varchar(6), isto što i PkgType ali sa nulama ispred. Dakle, PkgTzpe = 1 postaje PackageCode ‘01′. Pokušao sam ovako:

CREATE TABLE Test 

( 

ProjectId int NOT NULL CONSTRAINT ProjectId_must_be_51 CHECK(ProjectId = 51)
, PackageCode varchar (2) NOT NULL  

, PkgType int NOT NULL CONSTRAINT PkgType_is_PackageCode CHECK(PkgType = CAST(PackageCode AS int))
) 

Odgovor je bio:

Msg 8141, Level 16, State 0, Line 1 

Column CHECK constraint for column ‘PkgType’ references another column, table ‘Test’. 

Msg 1750, Level 16, State 0, Line 1 

Could not create constraint. See previous errors. 

 Pošto su u pitanju dve kolone, to ne može biti deo definicije kolone, to mora da je na nivou tabele.  Ovo je prošlo bez problema:

CREATE TABLE Test 

( 

ProjectId int NOT NULL CONSTRAINT ProjectId_must_be_51 CHECK(ProjectId = 51)
, PackageCode varchar (2) NOT NULL  

, PkgType int NOT NULL  

, CONSTRAINT PkgType_is_PackageCode CHECK(PkgType = CAST(PackageCode AS int))
) 

Razlika je u jednom zarezu kojim smo CHECK odvojili od definicije kolona.  Zbog čitljivosti, opredelio sam se za format

CREATE TABLE Test 

( 

– Kolone 

ProjectId int NOT NULL  

, PackageCode varchar (2) NOT NULL  

, PkgType int NOT NULL 

– Uslovi 

, CONSTRAINT PkgType_is_PackageCode CHECK(PkgType = CAST(PackageCode AS int))
, CONSTRAINT ProjectId_must_be_51 CHECK(ProjectId = 51)) 

 

Na kraju smo dobili izraz koji radi šta nam treba, lepo formatiran i jasan.

U sledećem javljanju pokazaćemo kako iz CHECK constraints mogu da se pozivaju druge tabele. Da, tačno je, ima i u Helpu, CHECK može da poziva i druge tabele. Ovo naravno ne znači da treba da zaboravimo na FOREIGN KEY i da sve prebacimo na CHECK. Medjutim ima situacija kada nam baš odgovara da kroz CHECK vidimo druge tabele.

 


MS SQL Common Table Expressions

January 4, 2008 – Zidar

MS SQl 2005 doneo je nekoliko značajnih novih rešenja. Jedna od novosti je upotreba Common Table Expressions (CTE). CTE su u stvari virtualne temp tabele. Šta ovo znači?

Pokušaću da pokažem na primeru o čemu se radi. Ne jednom forumu neko je postavio sledeće pitanje:

Zadate su tabele:

Partner(partner_sif#, ime, prezime)
Faktura(faktura_br#, partner_sif, faktura_datum_fakt)
Stavke_fakture(faktura_br#,roba_sif#,faktura_kolicina, faktura_cijena)

Traži se SQL upit koji za rezultat daje partnera s kojim je ostvaren najveći promet.

Pretpostavimo da je promet = suma(kolicina*cena)

Pretpostavka je da postoji tačno jedan partner sa najvećim prometom.

Zadatak bi se logički mogao razbiti na tri dela, ovako:

1. utvrditi koliki je promet ostvario svaki partner
2. izabrati najveći od svih prometa
3. utvrditi ko je partner sa najvećim prometom

Da krenemo:

Utvrdimo  koliki je promet ostvario svaki partner, is a;uvajmo SELECT kao view

CREATE VIEW v_Promet
AS
SELECT
partner_sif#
     , Promet = SUM(faktura_kolicina * faktura_cijena)
FROM Stavke_fakture

Sada utvrdimo koji je to promet najveći. Evo još jedan view, koji poziva onaj što smo napravili u prvom koraku.

CREATE VIEW v_NajveciPromet
AS
SELECT
     NajveciPromet = MAX(Promet)
FROM v_Promet

Sada ćemo utvrditi ko je partner sa najvećim prometom

SELECT
     partner_sif#
     , Promet
FROM v_Promet AS P
JOIN v_NajveciPromet AS M
ON M.NajveciPromet = P.Promet

Za sada sve izgleda relativno jasno. Nažalost,  DBA ne dozvoljava da kreiramo views, pa sve mormo da strpamo nekako u jedan SQL iskaz. Tu se stvari komplikuju. Rezultujući kveri mogao bi da izgleda ovako:

SELECT
        P.partner_sif#
        , P.Promet
FROM
        (SELECT
partner_sif#
, Promet = SUM(faktura_kolicina * faktura_cijena)
        FROM Stavke_fakture
         ) AS P
JOIN
        (SELECT
NajveciPromet = MAX(Promet)
FROM v_Promet
) AS M
ON M.NajveciPromet = P.Promet

Samo sam zamenio nazive view-a odgovarajucim SELECT iskazima. Ovo se zove upotreba in-line tabela.

Konacni SQL iskaz nije naročito komplikovan, ali nema baš previše ljudi koji mogu ovakav iskaz da napisu iz cuga. A dovoljno je nečitljiv, i pored truda uloženog u formatiranje.

SQL 2005 donosi rešeneje - CTE (Common Table Expressions). CTE su virtualne tabele, koje se definišu i žive u jednom baču. U našem slučaju, ono sto su bili views, postaće CTE.

Ovako to izgleda u našem zadatku:

WITH  CTE_Promet AS
(SELECT
    partner_sif#
     , Promet = SUM(faktura_kolicina * faktura_cijena)
FROM Stavke_fakture
)
,  CTE_NajveciPromet AS
(
SELECT
     NajveciPromet = MAX(Promet)
FROM CTE_Promet
)
SELECT
     partner_sif#
     , Promet
FROM CTE_Promet AS P
JOIN CTE_NajveciPromet AS M
     ON M.NajveciPromet = P.Promet

Mozete da zakljucite da sintaksa ide nekako ovako:

WITH
CTE_1 AS
(
SELECT koji definise CTE_1
)
, CTE_2 AS
(
SELECT koji definise CTE_2
ovde moze da se poziva prethodno definisani CTE
)
, CTE_X AS
(
SELECT koji definise CTE_X, koji
moze da poziva sve prethodno definisane CTE
)
SELECT f( CTE_1,  CTE_2,  f(CTE_X), završni izraz poziva bilo koji CTE)

Dakle, počinjemo službenom reči WITH. Onda definisemo jedan ili vise CTE virtualnih tabela. Svaki od prethodno definisanih može se upotrebit u definisanju sledećih. Na kraju pišemo izraz koji koristi bar jedan od definisanih CTE..

Na ovaj način se mnogi komplikovani izrazi sa subkverijima postaju mnogo jasniji. Ovakav jedan izraz, koji počinje sa WITH i definiše CTE može slobodno da se sačuva kao view, ili da se upotrebi u stored proceduri. Ako je u stored proceduri, svaki CTE može da koristi  koje god hoćete parametre.

CTE je dakle neka vrsta temp tabele koja postoji u jednom baču.

Postoji i jedna druga upotreba CTE, kad CTE sam sebe poziva i rezultat je sličan onome što proizvode iterativne petlje DO WHILE i kursori. Na ovaj način potreba za kursorima se svodi na minimum. Ako tržite HELP za CTE, videćete primer gde se CTE koristi kao alat za iterativno procesiranje hijerarhijskog skupa. Primer nije preterano jasan pa ću u neko skorije vreme pokušati da nešto napišem o tome.

:-)


Kloniranje/dupliciranje Oracle “ASM based” baze (How to duplicate Oracle “ASM based” database)

December 13, 2007 – Dejan

Pojavila se potreba da postojeću bazu klonirate, da biste mogli npr. izvršiti određene testove i provjeriti funkcionalnost novih programa. U ovom slučaju taj problem, odnosno zadatak, još kompleksnijim čini korištenje ASM (Automatic Storage Management), tako da postupak kloniranja baze nije jednostavan kao kod kloniranja baze sa datotekama na OS File-Systemu.

Sad se pitate:”Kako najlakše kopirati Oracle bazu, odnosno kako najefikasnije napraviti klon-bazu?“.

Klon-bazu možete kreirati na 3 načina:
1. koristeći RMAN i naredbu DUPLICATE
2. DBMS_FILE_TRANSFER paket
3. koristeći Enterprise Manager i opciju “Clone Database
4. pomoću “Cold backup” metode (rijetko se koristi, pogotovo u 24/7 okruženju)

Pročitaj kompletan tekst »


Ne volite fiziku i biologiju? A možda SQL?

November 24, 2007 – Zidar

Kompanija za koju radim prikuplja podatke o učenicima srednjih i osnovnih škola u Ontariju. Podaci koje prikupljamo menjaju se od godine do godine. Da ne bi menjali bazu svaki čas, imamo dve tabele:

CREATE TABLE Osobe
 (Ime varchar(25) NOT NULL PRIMARY KEY)
GO

CREATE TABLE Predmeti
 (Ime varchar(25) NOT NULL
 , Predmet varchar(25) NOT NULL
 PRIMARY KEY (Ime,Predmet)
 )
GO

U tabelama imamo ovo:

SELECT * FROM Osobe;
Ime
--------
Cica
Goca
Laza
Maca
Melanija
Pera
Verica
Zika   

(8 row(s) affected)

Za svaku osobu imamo podatke koje skupljamo, u ovom slučaju školske predmete:

SELECT Ime, Predmet
FROM Predmeti;

Ime        Predmet
———- ——-
Cica       Bio
Cica       Fiz
Laza       Bio
Laza       Geo
Laza       Math
Maca       Bio
Maca       Fiz
Maca       Geo
Maca       Math
Melanija   Geo
Pera       Bio
Pera       Fiz
Pera       Geo
Pera       Math
Verica     Bio
Verica     Math
Zika       Bio
Zika       Fiz
Zika       Math

Postavljeno je pitanje Ko NEMA predmet ‘Fiz’?

Klasično rešenje problema ide ovako:

SELECT Ime
FROM Osobe
WHERE Ime NOT IN(SELECT Ime
                   FROM Predmeti
                  WHERE Predmet = ‘Fiz’);

i daje korektno rešenje:

Ime
———
Goca
Laza
Melanija
Verica

(4 row(s) affected)

Neko je predložio da probamo da pokažemo sve osobe na levoj strani, a na desnoj njihove predmete, ali samo ‘Fiz’ i pogledamo gde je Predmet NULL. Probali smo prvo ovako:

SELECT A.Ime, B.Predmet
FROM Osobe AS A
LEFT JOIN Predmeti AS B
 ON A.Ime = B.Ime
WHERE B.Predmet = ‘Fiz’
AND B.predmet IS NULL;

što naravno nije vratilo ni jedan red. Ne može nešto da bude istovremeno ‘Fiz’ i NULL. Šteta, a delovalo je zaista obećavajuće. Onda se neko drugi setio da napiše upit ovako:

SELECT A.Ime, B.Predmet
FROM Osobe AS A
LEFT JOIN Predmeti AS B
ON A.Ime = B.Ime
AND B.Predmet = ‘Fiz’
WHERE B.predmet IS NULL;

Deo WHERE prebacili smo u deo JOIN ON. I dobili tačan rezultat iz cuga. Ništa subquery, ništa IN. Upravo smo otkrili način da zadamo WHERE i na desnoj strani LEFT JOINa.

Isti efekat mogli smo postići bez upotrebe temp tabela ovako:

WITH A AS
(SELECT Ime, Predmet = ‘Fiz’
   FROM Osobe
)
SELECT A.Ime, B.Predmet
FROM A
LEFT JOIN Predmeti AS B
ON A.Ime = B.Ime AND A.Predmet = B.Predmet
WHERE B.Predmet IS NULL

Problem je što ovo podrazumeva da znamo da upotrebimo CTE (common table expressions), pa da se setimo da ‘pomnožimo’ tabelu Osobe konstantom ‘Fiz’. Ostalo je lako. :)

Zato smo ostali kod našeg ‘proširenog JOINa’.

Onda smo proširili pitanje. Rekli smo ‘Ko NEMA ni jedan od predmeta (Fiz, Bio), dakle ni jedan od ova dva?

SELECT A.Ime, B.Predmet
FROM Osobe AS A
LEFT JOIN Predmeti AS B
 ON A.Ime = B.Ime
 AND (B.Predmet = ‘Fiz’ OR B.Predmet = ‘Bio’)
WHERE B.predmet IS NULL
Ime       Predmet
--------- -------
Goca      NULL
Melanija  NULL

(2 row(s) affected)

Elegantno, zar ne?

Pitali smo dalje Ko ima bar jedan od ova dva predmeta, (Fiz,Bio)?

Pokušali smo i dalje istim putem i za malo promašili:

SELECT A.Ime, B.Predmet
FROM Osobe AS A
LEFT JOIN Predmeti AS B
 ON A.Ime = B.Ime
 AND (B.Predmet = ‘Fiz’ OR B.Predmet = ‘Bio’)
WHERE B.predmet IS NOT NULL;

To nam je dalo zaista sve osobe koje imaju bar jedan od dva predmeta, ali su osobe koje imaju oba predmeta prikazane dva puta u izlaznom skupu:

Ime       Predmet
--------- -------
Cica      Bio
Cica      Fiz
Laza      Bio
Maca      Bio
Maca      Fiz
Pera      Bio
Pera      Fiz
Verica    Bio
Zika      Bio
Zika      Fiz

(10 row(s) affected)

Resili smo se duplikata na jednostavan način (minimum rada), ovako:

SELECT DISTINCT A.Ime —-, B.Predmet
FROM Osobe AS A
LEFT JOIN Predmeti AS B
 ON A.Ime = B.Ime
 AND (B.Predmet = ‘Fiz’ OR B.Predmet = ‘Bio’)
WHERE B.predmet IS NOT NULL;

Ubaciš DISTINCT, komentuješ Predmet i dobije se:

Ime
---------
Cica
Laza
Maca
Pera
Verica
Zika

(6 row(s) affected)

Postoji bolji način za eliminisanje duplikata. Zahteva malčice više rada u ovom momentu, ali donosi značajnu korist u sledećem koraku. Elem, ovako:

SELECT A.Ime , KolikoPredmeta = COUNT(*)
FROM Osobe AS A
LEFT JOIN Predmeti AS B
 ON A.Ime = B.Ime
 AND (B.Predmet = ‘Fiz’ OR B.Predmet = ‘Bio’)
WHERE B.predmet IS NOT NULL
GROUP BY A.Ime;
Ime    BrojPredmeta
------ -------------
Cica               2
Laza               1
Maca               2
Pera               2
Verica             1
Zika               2

(6 row(s) affected)

Dobili smo i broj predmeta, ko ima koliko od traženih dva predmeta. To može lepo da se iskoristi, ako se postavi pitanje Ko ima oba tražena predmeta?

Pa svi oni iz poslednjeg kverija koji imaju broj predmeta jednak 2. Ovako:

SELECT A.Ime , KolikoPredmeta = COUNT(*)
FROM Osobe AS A
LEFT JOIN Predmeti AS B
 ON A.Ime = B.Ime
 AND (B.Predmet = ‘Fiz’ OR B.Predmet = ‘Bio’)
WHERE B.predmet IS NOT NULL
GROUP BY A.Ime
HAVING COUNT(*)=2;
Ime   KolikoPredmeta
----- --------------
Cica               2
Maca               2
Pera               2
Zika               2

(4 row(s) affected)

Dopao mi se ovakav pristup, jer kad jednom u glavi svarite proširivanje JOIN filtera delom WHERE uslova, mnoga nezgodna pitanja postaju jednostavna. Međutim, najveća dobit jeste mogućnost da radite LEFT JOIN i da na desnoj strani ne dobijete baš sve redove, nego samo one koji vam trebaju. Ne sve predmete za svaku osobu, nego tačno onaj predmet koji vas u datom trenutku interesuje. Inače vam ne ginu temp tabele ili upotreba WITH.

Ako ovo nije bilo suviše suvoparno i zamorno, za koji dan pozabavićemo se upotrebom WITH, sa i bez rekurzije.


Tracing + TKProf = detaljna analiza za optimizaciju nekog (SQL) procesa

November 23, 2007 – Dejan

Ako pred sobom imate neku sporu aplikaciju, tj. neki spor SQL upit, u većini slučajeva ćete prvo pogledati šta kaže EXPLAIN PLAN, a ako ni nakon toga ne uočite potencijalni problem, onda u pomoć dolazi trejsovanje (tracing) i TKProf.

Pročitaj kompletan tekst »


Administrator Oracle baza podataka

November 16, 2007 – Dejan

Šta radi jedan administrator Oracle baza podataka? Kako izgleda jedan njegov radni dan?

Na nekoliko foruma sam već viđao ovakva pitanja, a u zadnje vrijeme sam ih dobio nekoliko putem E-Maila. Stoga ne bi bilo loše ukratko navesti osnovne zadatke jednog Oracle DBA i opisati kako izgleda jedan njegov radni dan.

Na Wikipediji imate veoma opširan opis jednog administratora baza podataka (”Database administrator“), a ja ću napisati samo stvari iz ličnog iskustva.

Kao Oracle DBA u firmi sam zadužen za slijedeće zadatke:

- Instalacija Oracle softwarea na serverima i klijentskim kompjuterima (za klijente sam napravio jednu silent install skriptu, kako ne bih morao ići od jednog kompjutera do drugog, pa se ta skripta može ručno pokrenuti ili putem jednog rollout updatea na nivou operativnog sistema)
- Testiranje novih komponenti i instalacija novih zakrpa (patching)
- Nadgledanje (monitoring) svih baza putem automatizovanih procesa i putem Enterprise Managera (Database Control i Grid Control)
- Praćenje performansi baze i aktivnih aplikacija, a potom optimizacija loših upita ukoliko su performanse loše
- Podrška programerima pri svakodnevnom radu (savjetovanje kako šta bolje da urade, konsultacije pri dizajniranju strukture tabela i drugih objekata, na šta treba da paze i td.); u sklopu ovog zadatka sam zadužen i za Acceptance i Development baze, te za replikaciju friških podataka sa Production baze na ove dvije, kako bi programeri imali uvijek nove podatke za ispravno testiranje i razvoj aplikacija; osim replikacije, koristim nekad i DataPump Export/Import
- Backup i recovery baze
- Svakodnevno praćenje security liste sa najnovijim propustima i rješenjima
- Kreiranje Service Requesta na MetaLinku ukoliko naletimo na neki Oracle bug
- Usavršavanje čitanjem knjiga i raznih članaka na internetu, kako bih novostečeno znanje prenio drugim kolegama (ja jedini volim da čitam Oracle knjige i dokumentaciju, da pratim razvoj Oracle softwarea i pronalaženje korisnih stvari, a onda kolege samo dođu i pitaju:”Dejane, kako da uradim ovo?” ili “Dejane, postoji li neka funkcija, pomoću koje se ovo može efikasno odraditi?“) - imaju sreće što volim da nesebično prenosim svoje znanje drugima. :D
- i td.

Jedan radni dan ne mogu generalno da opišem, jer mi nije svaki radni dan isti. Nekad provedem cijeli dan sarađujući sa podrškom iz Oracle Support putem MetaLinka, nekad provedem cijeli dan na optimizaciji nekog lošeg SQL upita ili čitave procedure, a nekad odradim masu sitnih stvari uključujući kratke sastanke, nadgledanje baze, provjeru backupa, analizu alert loga, pomaganje kolegama i sl.

U svakom slučaju, nije istina što ljudi (programeri uglavnom) kažu kako Oracle DBA efektivno dnevno radi samo 2 sata, a ostalo vrijeme provodi surfujući ili zujeći po kancelariji, odnosno po okolnim kafićima. :D
Nekad se radi čak i 3 sata!! ;) Just kidding.

Ako imaš odgovornost da Oracle baza mora biti dostupna i da sve radi što je bolje moguće, onda nema šanse da sve zadatke obaviš u samo 2-3 sata. Ja lično uglavnom nemam stresa i frke, ali kad se i to desi, ne traje dugo.

U svakom slučaju veoma zanimljiv posao, a ako imate kao i ja sreće da volite to šta radite i da ste još uz to dobro plaćeni, onda će vam zanimanje Oracle DBA zvučiti primamljivim.


Forum o dizajniranju baza podataka

November 15, 2007 – Srdjan

Gospodin Alf Pedersen (The Database Design Resource Center) je startovao forum o dizajniranju baza podataka. Forum se nalazi na adresi http://forum.databasedesign-resource.com/.

Nadam se da će forum biti kvalitetan i da će postati web lokacija koju ću rado posećivati.