Osnove SQL in njegova uporaba v perlu

Andraz Tori

Version 1.1 (10. April 2000)
Namen: Od slusateljev se pricakuje okvirno predznanje perla.
Predavanje se ne bo ukvarjalo z razlicnimi strezniki ter njihovo namestitivjo/konfiguracijo/administracijo. Prav tako ni namenjeno poucevanju o nacrtovanju podatkovnih baz, pac pa temelji v veliki meri na prakticnih izkusnjah in ne na teoriji.

0) Splosno

Baze podatkov so zelo komplexno podrocje zato nimamo casa za razlaganje teoreticnih osnov; za to obstajajo knjige. Tu gre za nekaksen hitri tecaj uporabe SQL jezika za potrebe pisanja razlicnih CGI skript, ad-hoc programov s pomocjo perla in pa tudi eventualno vecjih projektov.

Obstaja pisana paleta SQL streznikov, nekateri so komercialni, drugi so zastonj, tretji pa imajo mesano licenco.
SQL streznik na katerem bomo demonstrirali uporabo bo PostgreSQL 6.5.3 in prihaja z GPL licencno, torej je dostopen za vse uporabnike zastonj. Potrebno je vedeti, da pri programiranju manjsih projektov ni bistvenih razlik med razlicnimi strezniki. Kjer so razlike v osnovnih funkcijah SQL jezika bomo na to opozorili posebej.

Pri vecjih projektih velja dodobra preuciti kaksne so nase potrebe in vcasih ustrezno s tem poseci tudi po drazjih - komercialnih streznikih. Razlike med njimi bomo posebaj razdelali na koncu predavanja.

1) Uporaba SQL znotraj perla prek DBI/DBD

DataBase Interface ali na kratko DBI je vmesnik oziroma knjiznica, ki omogoca unificiran dostop do podatkovnih preko perla.

V nas program jo vkljucimo s stavkom:

use DBI;

Ko zelimo narediti povezavo s streznikom, izvrsimo preprosti ukaz:

$dbh = DBI->connect("DBI:Pg:dbname=database_name", "username", "password");

V prejsnjem primeru je privzeti streznik kar lokalen racunalnik. Vir kjer se nahaja streznik (prvi parameter metode connect) pa lahko bolj natancno dolocimo kot:

dbi:DriverName:database=database_name;host=hostname;port=port

Vedno je dobro tudi preveriti ali je ukaz uspel:

&HTMLerror("Cannot connect PostgreSQL SDSS database") unless $dbh;

Preverjanje napak bomo v prihodnjih primerih opustili, seveda pa je le to nujno za vsak resen projekt.

Povezavo na koncu dela prekinemo z :

$dbh->disconnect;

POZOR: obnasanje disconnecta je med razlicnimi bazami razlicno. Nekatere baze kot so Oracle in PostgreSQL avtomatsko izvedejo tudi ukaz commit, ki zapise vse spremembe v trenutni transkciji, medtem ko druge, naprimer informix tega ne naredijo in je nujno poklicati commit, preden povezavo prekinemo.

Vecina baz, ki jih bomo srecevali bo podpirala tako imenovane transakcije, ki nam omogocajo, da najprej koncamo z manipuliranjem baze, in sele ko se prepricamo, da se bodo vse spremembe izvedle uspesno, dejansko povzrocimo zapis vseh akcij. Transakcija se ob ukazu commit izvede 'atomsko', to pomeni, brez sprememb baze med delovanjem. V primeru, ko ne zelimo da se transakcija uposteva uporabimo ukaz rollback.

Privzeta nastavitev DBI knjiznice je tako imenovani autocommit, kar pomeni, da ne deluje v transakcijskem nacinu. Ce hocemo preklopiti v transakcijski nacin, nastavimo to z klicom:

$dbh->{AutoCommit}=0;

Nato v primeru, da ne zelimo, da se spremembe uposetavjo zazenemo: $dbh->rollback(); Sicer pa poklicemo: $sdss::dbh->commit();

No sedaj, ko znamo vzpostaviti povezavo z SQL bazo, lahko tudi izvrsimo prve SQL ukaze. S stavkom prepare pripravimo ukaz, ga nato izvrsimo z execute in izpisemo vse rezultate s pomocjo ukaza fetchrow:

$query=$dbh->prepare("SELECT uid,username FROM users");
$query->execute();
while (($uid,$username)=$query->fetchrow) {
 print ("Uid: $uid - Name: $username\n");
}

Kot vidimo z ukazom prepare najprej napovemo SQL ukaz, ki ga zelimo izvesti. (SQL jezik si bomo ogledali v nadaljevanju) Dejansko pa se ukaz izvede sele ob klicu metode execute.
Pri vsem skupaj si malo vec pozornosti zasluzi ukaz fetchrow, ki v obliki arraya vrne eno vrstico zahtevane poizvedbe. Ce smo izcrpali ze vse vrstice poizvedbe vrne vrednost nic.

Edini pomemben ukaz, ki nam je ostal, je do. Ta ukaz je v v bistvu le spoj prepare in execute Obicajno se uporablja pri SQL ukazih, ki ne vsebujejo selecta.

$dbh->do("INSERT INTO users VALUES ('janez','');

Ta stavek se bo izvrsil takoj, nam za njim ni treba klicati execute.

2) Programski jezik SQL

SQL ali Structured Query language je jezik, ki je bil razvit sredi 80ih let. Potreba po njem se je pokazala zaradi nekompatiblinosti med takratnimi strezniki podatkovnih baz. S pomocjo SQLa lahko tako zagotovimo, da bo aplikacija ostala razsirljiva tudi po tem ko preraste okvire zacetnega streznika in programja.
Trenutno je skupni imenovalec skoraj vseh streznikov, ki so na voljo, specifikacija SQL92, Vecina pa ponuja tudi svoje lastne bolj ali manj standerdizirane dodatke, ki omogocajo sicer hitrejse in udobnejse resevanje problemov, vendar so le-ti hkrati tudi dvorezen mec, saj nas priklenejo na enega samega ponudnika SQL streznika.
No pa gremo k jeziku samemu. Za zacetek je potrebno biti seznanjen z nekaj terminologije: No najvisja enota s katero se lahko manipulira znotraj jezika SQL pa ni baza pac pa tabela (ceprav nekateri strezniki kot naprimer PostgreSQL omogocajo tudi to).

CREATE

Torej je nas prvi cilj ustvariti tabelo s katero se bomo lahko 'igrali'. Primer tabele je v tem primeru baza uporabnikov nekega sistema.

CREATE TABLE users (
  uid int,
  username character varying(30),
  fullname character varying(30),
  password character varying(20),
  email character varying(50),
  subscribed timestamp,
  PRIMARY KEY (uid),
  UNIQUE (username)
)

Celoten ukaz bi lahko zapisali v eno vrstico, a ga zaradi citljivosti raje ne. Kot vidimo na koncu stavka ni nobenega posebnega indikatorja, ki bi kazal na to da je zakljucen. Razlog lezi v tem, da se ukazi strezniku posiljajo loceno (torej v paketih en po en), zato v jeziku SQL ni potrebe po dodatnem locevanju. Ce uporabljamo moznost uporabe SQLa iz ukazne vrstice, moramo navadno na koncu zapisati se podpicje, da interpretor ve kaj smo hoteli od njega.
Jezik SQL ni obcutljiv na razliko med velikimi in malimi crkami, a navadno se zaradi berljivosti vsi ukazi pisejo z velikimi.

No kot vidimo smo uporabili ukaz CREATE TABLE s katerim naredimo tabelo. Polja, ki jih bo imel vsak vnos v tabeli bodo uid, username, fullname, password, e-mail in cas vpisa.

Uporabili smo ze nekaj osnovnih tipov, pa povejmo se kaj natancno pomenijo:

INSERT


Poglejmo si kako v naso bazo vstavimo novo vrstico:

INSERT INTO users VALUES (1,'kjanez','Janez Kovac','slabogeslo','janez.kovac@siof.net',now()) INSERT INTO users VALUES (2,'nanica','Anica Novak','rozice','metka.novak@amor.net',now())

Torej, kot vidite smo preprosto navedli vsebino vseh polij, ki jih ima vsaka vrstica. Velja omeniti, da se nizi v SQL omejijo s pomocjo enojnih zgornjih narekovajev. Zaradi varnosti, nam perlov modul DBI ponuja funkcijo quote, ki nam vrne string v obliki, v kateri ni vec "escape sekvenc" (torej, vsebina stringa ne more vplivati na izvedbo SQL ukaza). Obstaja se druga - bolj profesionalna - resitev , ki se imenuje binding operatorjev, ki pa je tu ne bomo opisovali.

$stavek_q=$dbh->quote("My name's Mike");

SELECT

Seveda je glavni namen podatkovne baze moznost opravljanja vseh moznih vrst poizvedb. Za poizvedbe se uporablja ukaz SELECT, ki omogoca popoln nadzor nad vracanjem vrednosti, hkrati pa nudi moznost zelo kompleksnih poizved s pomocjo povezav med razlicnimi polji razlicnih tabel in podobno.

Najbolj enostavna oblika stavka SELECT je sledeca:

SELECT * FROM users

Stavek nam bo vrnil vsa polja vsake vrstice, ki je tabeli users. Zaporedje polij bo enako kot je bilo podano ukazu create. Kaksno bo zaporedje vrstic, ki bodo bile vrnjene ni definirano, zato se na to ne smemo zanasati.
Ce bi radi iz vsake vrstice dobili le polja uid in username bi bil ukaz SELECT naslednji:

SELECT uid,username FROM users

Ce pa bi radi, da bodo vrstice, ki jih bo SELECT vrnil prihajale v abecednem vrstnem redu, pri cemer bomo za kljuc vzeli fullname bomo ukazu dodali se ORDER BY dodatek:

SELECT uid,username FROM users ORDER BY fullname ASC

Tu ASC pomeni ascending, ce pa zelimo obrnjen vrstni red uporabimo DESC. Sedaj smo lahko prepricani, da bodo vrnjeni podatki vedno prihajali v enakem vrstnem redu. V zgornjem primeru bo vedno vrnjena najprej vrstica (2,'nanica') in nato (1,'kjanez').

Ce bi radi izpisali le eno (prvo) vrstico, lahko omejimo stevilo vrnjenih odgovorov z dodatkom LIMIT.

SELECT uid,username FROM users ORDER BY fullname ASC LIMIT 1

Sedaj pa si pogljemo se iskanja po tabeli. Na primer, ce zelimo ugotoviti, kaksen je fullname uporabnika 'kjanez', bomo to storili z naslednjim ukazom:

SELECT fullname FROM users WHERE username='kjanez'

Z dodatkom WHERE dolocamo omejitv poizvedbe. Za njim lahko sledi poljuben izraz, ki ga zna SQL evaluirati.
Ker je kompletna mnozica izrazov s katerimi lahko operiramo zelo velika to ni primeren kraj za podrobnejse opise, priporocam pa ogled PostgreSQL user's guide v katerem so izjemno natancno opisani vsi mozni ukazi in izrazi, ki jih lahko uporabljamo v SQL. Mimogrede bi se omenil, da bi se lahko veliko free software projektov zgledovalo po PostgreSQL kar se tice dokumentacije, saj je le ta res vzorna (99% stvari, ki jih vem o SQL sem se naucil tam).

Kompleksnejse poizvedbe s SELECT

SELECT omogoca tudi pozivedbe, ki povezujejo razlicnime tabele. Tako bi lahko na primer, ce dodamo se tabelo zivali in v njej polje lastnik, ki ima enako vsebino kot username v tabeli users, naredili poizvedbo katere vse zivali ima lastnik, katerega uid je 14:

SELECT zivali.ime FROM zivali,users WHERE users.uid=14 AND zival.lastnik=users.username

Stetje s SELECT

Ce zelimo, ugotoviti koliko vrstic v tabeli ustreza dolocenim zahtevam lahko uporabimo v SELECT-u posebno funkcijo count(*):

SELECT count(*) FROM users WHERE uid<50

Zgornji stavek bo izpisal stevilo vrstic, v katerih je vrednost polja uid manjsa od 50.
V primeru, da zelimo sesteti skupno vrednost vseh polij, ki ustrezajo pogojem lahko uporabimo ukaz sum(polje):

SELECT sum(uid) FROM users

Ukaz bo vrnil skupen sestevek vseh uid polij v tabeli users. (Ta podatek seveda nima nobene pomenske vrednosti, je le primer uporabe)

DELETE

No ker vcasih zelimo kaj iz tabel tudi zbrisati, uporabljamo za to ukaz DELETE. Uporaba je relativno enostavna:

DELETE FROM users WHERE uid>40 AND uid<50

Ta stavek bo naprimer izbrisal iz tabele users vse vrstice, ki imajo uid vecji od 40 in hkrati manjsi od 50.

UPDATE

Ce zelimo dolocene vrstice le spremeniti, uporabimo ukaz UPDATE:

UPDATE users SET uid=uid+1000 WHERE uid<100

Zgornji ukaz bo vsa polja uid, ki so manjsa od 100 nastavil na prejsnjo vrednost + 1000.

3) Triki

Listanje po tabeli

Kadar bi radi v tabeli listali naprimer po 10 vrstic lahko poleg LIMIT dodatka, uporabimo se OFFSET.

SELECT uid,username FROM users ORDER BY fullname ASC LIMIT maxlist OFFSET odmik

Tak stavek nam bo vrnil le tiste vrstice, ki bi sicer bile po vrsti med odmik in odmik+maxlist, ce bi prebrali celotno zaporedje vrstic. Seveda tu nujna uporaba ORDER BY, saj bi sicer lahko podatki ob vsaki poizvedbi prihajali v popolnoma nakljucnem vrstnem redu.

Sporocanje napak

Prva stvar, ki jo bomo potrebovali pri pisanju cgi skript v perlu bo verjetno sporocanje napak prek weba, v ta namen lahko za zacetek uporabimo zelo enostavno funkcijo HTMLerror:

sub HTMLerror {
 print "Content-type: text/html; charset=iso-8859-2\n\n";
 print "<HTML><BODY>";
 print "<BR> Error: @_<BR>";
 print "<A HREF=\"JavaScript:history.back()\">Go back wherever you come from</A>";
 print "</BODY></HTML>";
 dbdisconnect;
 exit;
}

4) Kdaj SQL?

Preden zacnemo pisati projekt je potrebno ugotoviti ali je SQL sploh resitev za nase potrebe.
Zavedati se je treba, da tudi nekateri najbolj varcni strezniki poberejo kar nekaj dragocenih sistemskih sredstev.
Prvo pravilo je, da ce ne potrebujemo nicesar vec kot osnovnih poizvedb po podatkih, naceloma ne potrebujemo SQL. Osnovne poizvedbe so tu misljene predvsem kot neposredno iskanje po poljih. Cim se lotimo naloge kriznega indexiranja ali cesarkoli podobnega je uporaba SQL nadvse priporocljiva, saj nam bo velikokrat prihranila ogromno dragocenega casa in zivcev.

Za primer programa pri katerem je SQL odvec lahko vzamemo kar prijavljanje na Linux Fest 6. Najprej sem sicer imel namen aplikacijo napisati kot SQL odjemalec, vendar je postalo jasno, da za nalogo ni potrebno, razen vpisovanja v textovno datoteko in prikaz celotne datoteke, nic drugega. Manipuliranje s textovnimi datotekami je v tem primeru v perlu mnogo lazje, hitrejse in ucinkovitejse.

Ko pa se lotimo vecjih stvaritev - naprimer web oglasne deske ali cesa podobnega, kjer je potrebno imeti vec razlicnih strukturiranih podatkov, vcasih kaj spremeniti ali celo izbrisati, pa se hitro pokaze, da je SQL verjetno najhitrejsa in tudi najcenejsa resitev.
Najbolj pomembno je, da pazimo in ne izumljamo tople vode znova in znova. Nekaj ur ucenja SQLja lahko prihrani nekaj tednov pisanja lastnih rutin za tisto, kar so nekateri ze zdavnaj naredili in to dobro!

5) Kateri streznik uporabiti?

Kateri streznik podatkovnih baz je primeren za nas projekt je seveda odvisno od ogromnega stevila razlicnih faktorjev. Pogledali si bomo tri razlicne izdelke, eden pod licenco GPL, drugi pod delno prosto in delno komercialno licecno ter tretjega popolnoma komercialnega (ki pa ga lahko za domaco preizkusino vseeno uporabljamo).
PostgreSQLMySQLOracle
Cena: GPL (popolnoma zastonj)stara verzija GPL
Windows verzija shareware
ne-Windows verzije so zastonj razen v primerih, ko prodajate izdelke ali storitve, ki uporablajo MySQL
od 100 USD dalje
Dokumentacija: Izjemno dobra on-line dokumentacija Izjemno dobra on-line dokumentacija ???
Komercialna podpora dadada
Hitrost: Pri razvoju ni bila prvotnega pomena, zato za obema drugima zaostaja najhitrejsa???
Omejitve: jih nijih ni?jih ni
Za uporabnike pod linuxom je verjetno najenostavnejse uporabljati kar PostgreSQL ali MySQL, saj ta dva navadno ze obstajata v linux distribuciji.

PostgreSQL je bil na zacetku razvoja osnovan predvsem kot raziskovalni projekt zato ponuja nekatere vcasih malce cudaske moznosti. Raziskovalni znacaj se mu se vedno pozna, saj nekatere lasnosti se vedno niso skladne s standardom SQL92, da o SQL3 ne govorimo.

Oracle je svojo podporo za Linux udejanil sele lansko leto, ko je dejansko izdal delujoce programje. Glede na visjo ceno, ki jo je potrebno placati za ta komercialni produkt je morda vredno premisliti ali se bo investicija obrestovala. Je pa potrebno tudi vedeti, da edino pri oraclu lahko dobimo podporo tudi v Sloveniji, medtem, ko se je pri ostalih dveh bazah potrebno zanesti predvsem na elektronske medije.

Natancne primerjave predvsem o konformnosti s standardom SQL na podlagi realnih testov in informacije o drugih omejitvah lahko dobimo na http://www.mysql.com/crash-me-choose.htmy

Viri:
www.mysql.com
www.postgresql.org
www.oracle.com

10) Nadalnje ctivo:

SQL:
DBI home page
DBI Specifikacija
PostgreSQL user's guide

Perl:
Perl tutorials (tudi DBI)
Zelo dobra on-line knjiga "Introduction to Perl & CGI Programming"

by Andraz Tori, stvar je frej in z njo lahko pocnete kakrkoli vam pac pase!