Data ble først lagret på sekvensielle filer, der postene legges etter hverandre på en fil uten noen relasjon mellom postene.
Når postene skulle leses, måtte man starte på begynnelsen, og lese en og en post i samme rekkefølge som de var lagt ut.
Livet som norsk COBOL-utvikler på 80- og 90-tallet
Per-Arne (70) om timeslang kompilering, ukeslang debugging og år 2000-problemet.
Databaser kjennetegnes ved at hver post har en nøkkel, der ett eller flere felter i kombinasjon utgjør en entydig informasjon.
De første databasene hadde en ren trestrektur, også kalt hierarkisk modell. Poster i trestrukturen lar seg linearisere, det vil si at de kan lagres etter hverandre på sekvensielle medier, for eksempel magnetbånd. Programmereren måtte skrive kode for traversering av treet for å finne aktuell post, eller legge en ny post på riktig plass i treet.
Senere fikk man adresserbare disker, og da ble det mulig å hoppe fra én post til en annen ved hjelp av pekere. Med denne teknologien innførte man nettverksdatabaser. Programmereren måtte skrive kode for manøvreringen i nettverket.
Med de gamle programmeringsspråkene lagde man gjerne en løkke, der man hentet opp en post, behandlet denne ferdig, før man hentet neste post og behandlet denne på tilsvarende måte.
Realasjonsdatabaser og SQL
Relasjonsdatabasene ble populære midt på 80-tallet.
Relasjonsdatabaser består av tabeller som er forbundet med nøkler mellom seg.
Oracle sin relasjonsdatabase ble raskt markedsledende, og Oracle Norge ble opprettet 1.mai 1988, men allerede fra 1985 var Oracle representert i Norge gjennom firmaet Scansoft. Opprinnelig het firmaet EB Scansoft, da den norske hjørnestensbedriften innen telefoni, Elektrisk Bureau, var hovedaksjonær.
SQL - Structured Query Language - var det nye spørrespråket mot relasjonsdatabaser, som er basert på relasjonsalgebra. I sin enkelhet har det kun 4 operasjoner mot data:
- insert, sette data inn i en databasetabell
- delete, slette data fra en databasetabell
- update, oppdatere data i en databasetabell
- select, velge ut data fra en eller flere databasetabeller
Fra prosedyre til mengde
Overgangen fra prosedyreorientert programmering til mengdeorientert programmering ble en stor utfordring for mange programmerere.
Nå behandlet du ikke lenger en og en post, men en mengde poster i én operasjon. Du måtte skrive where-betingelser, som avgrenset hvilke poster som skulle behandles i operasjonen.
På et oppdrag hos Norske Shell jobbet jeg med dokumentkontrollsystemet for Draugen plattformen. Dokumentkontrollsystemet inneholdt viktige attributter om det enkelte dokument. Operatørene kunne søke på disse attributtene for å finne fram til relevante dokumenter. Og her snakket vi om enorme mengder med dokumenter.
«Å godkjenne oppdateringen gjennom oppdateringsbildet hadde en prosesserinsgstid på nesten 15 minutter.»
Under lunsjen en dag kom jeg i prat med en av personene som scannet dokumenter, og la inn attributtene til disse. Hun hadde fått en ensformig og tidkrevende oppgave.
Det var en leverandør som hadde kjøpt opp en annen leverandør, og nå var det ønskelig at det gamle leverandørnummeret ble oppdatert for alle dokumenter der det var lagret.
Med et søkebilde hadde hun funnet at det var 142 forekomster som måtte endres, men oppdateringsbildet hun brukte kunne bare oppdatere en forekomst av gangen.
Å godkjenne oppdateringen gjennom oppdateringsbildet hadde en prosesserinsgstid på nesten 15 minutter. Hun hadde kun fått endret 8 forekomster denne formiddagen, og kom til å bruke mer enn en uke for å få endret alle forekomstene.
Jeg ba henne bli med meg bort til min arbeidsplass. Der søkte jeg opp antall forekomster med det gamle leverandørnummeret, og fant som forventet 134 stykker, hun hadde jo allerede endret 8. For sikkerhets skyld skrev jeg ut dokumentnumrene for disse på papir, så skrev jeg følgende SQL kode:
Disse databasene anbefaler norske utviklere
UPDATE dokument
SET leverandornr = <nytt nummer>
WHERE leveradornr = <gammelt nummer>
Jeg ba henne, som ansvarlig fagperson, å lese korrektur for å være helt sikker på at både gammelt og nytt leverandørnummer var riktig.
Det bekreftet hun, og jeg utførte koden og fikk etter 2 minutter svaret «134 rows updated».
Jeg godkjente oppdateringen og fortalte at nå var alle forekomstene endret. Hun ville derimot ikke tro på at jeg hadde oppdatert mer enn én forekomst, og mente at det fremdeles gjenstod 133 forekomster.
Hun fikk med seg utskriften med dokumentnumrene for å sjekke. En halv time senere ringte hun tilbake og sa at alle forekomstene faktisk var rettet, men hun skjønte ikke hvordan jeg hadde klart det.
Kople sammen flere tabeller
Så lenge du kun jobber mot en tabell, er SQL programmeringen enkel. Utfordringene får du når du skal kople sammen flere tabeller i samme operasjon.
Dersom tabellene har identiske attributter, kan du bruke mengdeoperasjonene union, snitt og differense.
Eller du kan bruke where-betingelsene til å beskrive koplingene, enten direkte kopling av attributter eller ved bruk av subselect. Det er også mulig å legge nye subselecter inn i subselecter. Da kan koden bli svært komplisert.
«I hine hårde dager var det kun én tilgjengelig metode: prøving og feiling.»
Det er fort gjort å få et uønsket kartesisk produkt, at alle elementer i en tabell pares med alle elementene i en annen tabell.
Ytelse blir fort en utfordring. Ved riktig bruk av indekser kan du unngå at hele tabellen gjennomløpes for å finne de aktuelle forekomstene, men man kan også risikere at indeksene slår hverandre i hjel og dermed ikke virker som forventet.
I dag finnes det mange gode verktøy for å analysere effekten av indekser og andre tuningstiltak, men i hine hårde dager var det kun én tilgjengelig metode: prøving og feiling.
Ofte satt vi med stoppeklokka på kveldstid, da det ikke var annen aktivitet på maskinen, for å måle hvilken variant av koden som eksekverte raskest.
- Vi sitter på helt syke datamengder
Vi fant eksempelvis ut at rekkefølgen tabellene ble nevnt i select-statementet kunne ha stor betydning, likeledes rekkefølgen på linjene i where-betingelsene.
Når det skulle gjøres mange forskjellige oppdateringer i en tabell, og oppdateringene var avhengig av informasjon fra flere andre tabeller, brukte vi ofte en hjelpetabell for å få behandlingen til å gå raskere.
Vi kjørte en tung select først for å populere hjelpetabellen med nødvendig informasjon fra de øvrige tabellene. Deretter la vi flere update-statements etter hverandre hvor hovedtabellen kun ble koplet mot hjelpetabellen. Til slutt kunne vi slette hjelpetabellen.
Embedded SQL
Med embedded SQL kan du skrive SQL-statements innenfor kildekoden til et annet programmeringsspråk. På denne måten kan du benytte fleksibiliteten i programmeringsspråket og samtidig utnytte SQL sine datamanipuleringsmuligheter.
«Da embedded SQL ble tilgjengelig, ble det faktisk mulig å programmere prosedyreorientert med SQL.»
Pro*C ble ofte benyttet sammen med Oracle SQL.
Jeg har tidligere fortalt at overgangen fra prosedyreorientert programmering til mengdeorientert programmering ble en utfordring for mange programmerere.
Da embedded SQL ble tilgjengelig, ble det faktisk mulig å programmere prosedyreorientert med SQL.
I mitt første store SQL utviklingsprosjekt, var vi et team som hadde skrevet mange flotte SQL programmer basert på mengdeorientering. Da teamet besto av eksterne konsulenter, skulle vi etter hvert fases ut av firmaet.
Programvedlikeholdet skulle utføres av firmaets egne programmerere, som ikke hadde erfaring med mengdeorientert programmering fra før. Opplæringen av dem var krevende.
Noen av dem forstod absolutt ikke logikken i programmene våre, og så kom sjefsavgjørelsen: Før vi forsvant ut, måtte vi skrive om alle programmene til embedded SQL med Pro*C og prosedyreorientert logikk.
Autocommit
Jeg hadde vært med å skrive noen SQL programmer for en bedrift med kontorer flere steder i Norge. Programmeringen hadde tatt lenger tid enn planlagt.
Uheldigvis hadde noen ledere annonsert når de nye programmene ville være tilgjengelig, og det var tydeligvis mye prestisje for dem at denne datoen ble overholdt, derfor ble programmene satt i produksjon før nødvendig testing var gjennomført.
Resultatet ble som det måtte bli. Frustrerte brukere opplevde mange feilsituasjoner som ble meldt til oss utviklere. Vi satt lange kvelder og korrigerte datafeil, samt forsøkte å rette programfeilene.
«Øynene mine gikk i kryss, hodet verket. Jeg så på klokka, den var 01:15.»
Sent en kveld hadde jeg logget meg på mot Ålesund-kontoret. Det hadde blitt noen feilregistreringer innenfor en varegruppe som jeg skulle rette opp.
Jeg lagde da en SQL-snutt som skulle fikse dette, men jeg skvatt litt da jeg kjørte den: «15 717 records updated».
Rettelsen skulle da bare påvirke cirka 50 forekomster, SQL-en min var tydeligvis ikke vellykket. Det var best å kjøre en rollback med en gang, slik at den feilaktige oppdateringen ble reversert. «0 records rolled back».
Da oppdaget jeg at katastrofen var fullendt, på denne maskinen var funksjonen autocommit slått på, noe som var uvanlig. Det var ingen angremulighet.
Jeg satte meg ned og skrev en ny SQL som skulle reversere effekten av den forrige. Dette var vanskelig, men endelig var jeg klar til å kjøre den.
Ytelse og kode - dette bør du tenke på
Den kvernet i lang tid, før den langt om lenge aborterte pga ressursmangelen. SQL-spørringen var for krevende, den måtte deles opp i flere mindre SQL-er.
Øynene mine gikk i kryss, hodet verket. Jeg så på klokka, den var 01:15. Dette var absolutt ikke tidspunktet for å skrive krevende SQL-kode. Jeg sendte følgende melding til min kontaktperson i Ålesund før jeg reiste hjem: "Beklager, men jeg har gjort en tabbe. Hvis dere søker etter varer i varegruppe XX, vil dere få beskjed om at det ikke finnes varer på lageret. Dette behøver ikke å stemme. Jeg kommer tilbake senere i dag og retter feilen".
Enhver programmerer vet at det kan være lurt å sove på et problem.
Underbevisstheten er ofte mer effektiv enn bevisstheten. Og det slo til nå også. Selv om jeg sov urolig, bråvåknet jeg kl 06 og så SQL-koden klart for meg. Jeg skyndte meg på jobb, og kl 07:45 ringte jeg mannen i Ålesund og kunne stolt fortelle at nå var problemet løst. Han lo godt av hele episoden.
SQL som genererer SQL kode
Det finnes mange måter å effektivisere et program på, men ikke alle er like vedlikeholdbare.
En noe spesiell teknikk - som jeg må innrømme at jeg noen ganger har brukt selv også - er å lage en SQL-spørring som genererer SQL-kode. Og den nye koden eksekveres umiddelbart etterpå.
Jeg hadde en kollega som dro denne teknikken et skritt videre. Han skrev en SQL-spørring som genererte ny SQL-kode. Denne ble kjørt, og genererte enda en ny SQL-kode. Som i sin tur ble kjørt. Dette programmet hadde fungert bra lenge. Men så, mens kollegaen min var på HV-øvelse, feilet programmet. Og jeg ble den heldige vinner av feilsøkingen.
Det tok sin tid, men jeg klarte faktisk å finne feilen.
Slik koda Per-Arne (70) på 70-tallet
Pensjonisten om programmering på papir, hullkort og GO TO-misbruk i Fortran.