April 2002 Nr 11, Årgang 3 ISSN 1600-5147 Pris: kr. 125,00 ex moms www.oracleekspert.dk FEKS. TIL NEM REGRESSIONSTEST AF SYSTEMOPDATERINGER 4



Relaterede dokumenter
IBM Network Station Manager. esuite 1.5 / NSM Integration. IBM Network Computer Division. tdc - 02/08/99 lotusnsm.prz Page 1

Portal Registration. Check Junk Mail for activation . 1 Click the hyperlink to take you back to the portal to confirm your registration

Aktivering af Survey funktionalitet

Shooting tethered med Canon EOS-D i Capture One Pro. Shooting tethered i Capture One Pro 6.4 & 7.0 på MAC OS-X & 10.8

DET KONGELIGE BIBLIOTEK NATIONALBIBLIOTEK OG KØBENHAVNS UNIVERSITETS- BIBLIOTEK. Index

Privat-, statslig- eller regional institution m.v. Andet Added Bekaempelsesudfoerende: string No Label: Bekæmpelsesudførende

User Manual for LTC IGNOU

Help / Hjælp

Vores mange brugere på musskema.dk er rigtig gode til at komme med kvalificerede ønsker og behov.

E-PAD Bluetooth hængelås E-PAD Bluetooth padlock E-PAD Bluetooth Vorhängeschloss

Black Jack --- Review. Spring 2012

The X Factor. Målgruppe. Læringsmål. Introduktion til læreren klasse & ungdomsuddannelser Engelskundervisningen

Project Step 7. Behavioral modeling of a dual ported register set. 1/8/ L11 Project Step 5 Copyright Joanne DeGroat, ECE, OSU 1

Basic statistics for experimental medical researchers

Engelsk. Niveau D. De Merkantile Erhvervsuddannelser September Casebaseret eksamen. og

Engelsk. Niveau C. De Merkantile Erhvervsuddannelser September Casebaseret eksamen. og

how to save excel as pdf

Brug sømbrættet til at lave sjove figurer. Lav fx: Få de andre til at gætte, hvad du har lavet. Use the nail board to make funny shapes.

Vina Nguyen HSSP July 13, 2008

GUIDE TIL BREVSKRIVNING

CHAPTER 8: USING OBJECTS

Vejledning til Sundhedsprocenten og Sundhedstjek

Unitel EDI MT940 June Based on: SWIFT Standards - Category 9 MT940 Customer Statement Message (January 2004)

Titel: Barry s Bespoke Bakery

WIKI & Lady Avenue New B2B shop

Userguide. NN Markedsdata. for. Microsoft Dynamics CRM v. 1.0

DK - Quick Text Translation. HEYYER Net Promoter System Magento extension

PARALLELIZATION OF ATTILA SIMULATOR WITH OPENMP MIGUEL ÁNGEL MARTÍNEZ DEL AMOR MINIPROJECT OF TDT24 NTNU

Vejledning til at tjekke om du har sat manuel IP på din computer.

LESSON NOTES Extensive Reading in Danish for Intermediate Learners #8 How to Interview

Hvor er mine runde hjørner?

Bilag. Resume. Side 1 af 12

Begrænsninger i SQL. Databaser, efterår Troels Andreasen

Det er muligt at chekce følgende opg. i CodeJudge: og

Design til digitale kommunikationsplatforme-f2013

DSB s egen rejse med ny DSB App. Rubathas Thirumathyam Principal Architect Mobile

3D NASAL VISTA TEMPORAL

3D NASAL VISTA 2.0

Øvelse 9. Klasser, objekter og sql-tabeller insert code here

MSE PRESENTATION 2. Presented by Srunokshi.Kaniyur.Prema. Neelakantan Major Professor Dr. Torben Amtoft

QUICK START Updated:

QUICK START Updated: 18. Febr. 2014

Before you begin...2. Part 1: Document Setup...3. Part 2: Master Pages Part 3: Page Numbering...5. Part 4: Texts and Frames...

Boligsøgning / Search for accommodation!

Improving data services by creating a question database. Nanna Floor Clausen Danish Data Archives

Database. lv/

Trolling Master Bornholm 2015

Skriftlig Eksamen Kombinatorik, Sandsynlighed og Randomiserede Algoritmer (DM528)

Accessing the ALCOTEST Instrument Upload Data - NJSP Public Website page -

Trolling Master Bornholm 2016 Nyhedsbrev nr. 3

SPECIALTRYKKERIETS KUNDE WEBPORTAL KOM GODT I GANG

RentCalC V Soft-Solutions

VÆR EFFEKTIV SOM SAS PROGRAMMØR MED SAS ENTERPRISE GUIDE 7.12 GEORG MORSING

Danish Language Course for Foreign University Students Copenhagen, 13 July 2 August 2016 Advanced, medium and beginner s level.

Online kursus: Content Mangement System - Wordpress

Danish Language Course for International University Students Copenhagen, 12 July 1 August Application form

Trolling Master Bornholm 2013

The River Underground, Additional Work

De vigtigste SQL-sætninger. SQL kap Oprette database. DDL og DML

The purpose of our Homepage is to allow external access to pictures and videos taken/made by the Gunnarsson family.

Trolling Master Bornholm 2013

CS 4390/5387 SOFTWARE V&V LECTURE 5 BLACK-BOX TESTING - 2

Den nye Eurocode EC Geotenikerdagen Morten S. Rasmussen

Terese B. Thomsen 1.semester Formidling, projektarbejde og webdesign ITU DMD d. 02/

Status på det trådløse netværk

Microsoft Dynamics C5. version 2012 Service Pack 01 Hot fix Fix list - Payroll

ECE 551: Digital System * Design & Synthesis Lecture Set 5

WIFI koder til Miljøagenturet: Brugernavn: AIACE course Kodeord: TsEG2pVL EU LOGIN KURSUS 21. AUGUST FORMIDDAG:

1 Indlæsning af script

Observation Processes:

USER GUIDE Version 2.9. SATEL Configuration Manager. Setup and configuration program. for SATELLINE radio modem

How Long Is an Hour? Family Note HOME LINK 8 2

BACK-END OG DATA: ADMINISTRATION HVAD ER DE NYE MULIGHEDER MED VERSION 7.1? STEFFEN BILLE RANNES, 4. FEBRUAR 2015

Besvarelser til Lineær Algebra Reeksamen Februar 2017

On the complexity of drawing trees nicely: corrigendum

DANSK INSTALLATIONSVEJLEDNING VLMT500 ADVARSEL!

Tema: Pets Fag: Engelsk Målgruppe: 4. klasse Titel: Me and my pet Vejledning Lærer

Trolling Master Bornholm 2012

Fejlbeskeder i SMDB. Business Rules Fejlbesked Kommentar. Validate Business Rules. Request- ValidateRequestRegist ration (Rules :1)

Trolling Master Bornholm 2015

ATEX direktivet. Vedligeholdelse af ATEX certifikater mv. Steen Christensen

MultiProgrammer Manual

Overfør fritvalgskonto til pension

TM4 Central Station. User Manual / brugervejledning K2070-EU. Tel Fax

Business Rules Fejlbesked Kommentar

Titel Stutterer. Data om læremidlet: Tv-udsendelse 1: Stutterer Kortfilm SVT 2, , 14 minutter

Dean's Challenge 16.november 2016

Remember the Ship, Additional Work

User guide - For testing SFTP and HTTP/S data communication

RPW This app is optimized for: For a full list of compatible phones please visit radiation. result.

Molio specifications, development and challenges. ICIS DA 2019 Portland, Kim Streuli, Molio,

Oracle PL/SQL. Overview of PL/SQL

Blomsten er rød (af Harry Chapin, oversat af Niels Hausgaard)

Forslag til implementering af ResearcherID og ORCID på SCIENCE

Fejlbeskeder i Stofmisbrugsdatabasen (SMDB)

Linear Programming ١ C H A P T E R 2

KUNDE-WEBPORTAL KOM GODT I GANG

Udbud på engelsk i UCL. Skabelon til beskrivelse

Titel: Hungry - Fedtbjerget

En Kort Introduktion til Oracle

Transkript:

April 2002 Nr 11, Årgang 3 ISSN 1600-5147 Pris: kr. 125,00 ex moms www..dk #11 OUGDK 23 OUGDK Stormøde Næste møde er endnu ikke fastlagt. DBA SIG Næste møde er endnu ikke fastlagt. Designer SIG Næste møde:14. august 2002 kl. 13:00 Developer SIG Næste møde er endnu ikke fastlagt. Data warehouse SIG Næste møde: 12. juni 2002 Web SIG Næste møde er endnu ikke fastlagt. NYHEDER 22 Oracle uddeler legater Oracle dropper Andersen JDeveloper vinder tre priser PC Magazine kårer Oracle9i Oracle ERP kåres som favorit af AMR Research Oracle støtter Microsoft-udviklere Ny verdensrekord for SAP DS TABEL-SAMMENLIGNINGER M. SQL - FEKS. TIL NEM REGRESSIONSTEST AF SYSTEMOPDATERINGER 4 Bodil Feldinger Det er ofte interessant at kunne sammenligne indholdet af strukturmæssigt ens tabeller. F.eks til identifikation af forskellene mellem et programs database-opdateringer før hhv. efter system-ændring (del af regressionstest). Her er en simpel lille værktøjskasse med SQL er, der kan tilpasses og anvendes til sådanne sammenligninger, både til overblik og detaljeret visning af forskelle. UPDATE THE DESIGNER REPOSITORY THROUGH THE WEB 13 Marc de Oliveira The Designer Repository is a very good location for keeping structured information about your business's application systems and their related elements like requirements, time plans, status information and other types of application system related information. Much of this information is naturally supplied by developers who use Designer for designing and developing the application systems but some things are best maintained by managers, end users, customers and others who do not know how to use Designer. AFVIKLINGSPLAN OG TRÆSØGNING 20 Martin Jensen Kender du det? Man sidder og kigger på databasesystemets tungeste SQLsætninger i et eller andet værktøj, og får lyst til at se hvorledes database-systemets afviklingsplan for sætningen egentligt ser ud. Man trækker så sætningen over i en SQL*Plus session, og prøver med passende bind-variable at bede om en explain plan. Men her kører sætningen hurtigt? Indryk en stillingsannonce i DKK 1500 (for 1/4 side) Salg@.dk

Leder AFLYST Marc de Oliveira, ansvarshavende redaktør. Så kom krisen også til. Trods stor velvilje fra forfatterne lykkedes det ikke at tiltrække de 50 deltagere, som var nødvendige for at gennemføre -konferencen. Samtidig er tilstrømningen af abonnenter stort set gået i stå, og der er meget langt mellem annoncerne. Også alle andre kan mærke problemerne. Primært er salget af konsulenter tilsyneladende meget mærket, hvilket har givet sig udslag i daglige opkald fra de forskellige konsulentbureauer, om man nu ikke har behov for et par konsulenter i et stykke tid. Også Oracles aktier har haft en meget dårlig periode i de sidste to måneder, hvor de næsten har halveret deres værdi fra ca 18 dollars til de nuværende 11 (selv om de nu også var en tur dernede i september sidste år). Der er lang vej op til de 45 dollars, som de var værd, da nr 1 blev sendt på gaden. Ja, der er nok at græde over. Samlet set kunne det tyde på at, op mod sin to års fødselsdag, går en meget dyster fremtid i møde, når vi samtidig har et mål om at have mindst 300 abonnenter inden årets udgang. Derfor vil vi i den næste tid iværksætte en række nye initiativer, som skal gøre til et endnu mere interessant blad. Blandt de nye initiativer kan nævnes: En kampagnepris for nye abonnenter, der tegner abonnement inden 1. august, som inkluderer en signeret og nummereret kopi af en Oraclerne-strip. Spændende Oracle-relaterede afstemninger for abonnenterne på vores hjemmeside (www..dk). Adgang til -artiklerne i elektronisk format fra vores hjemmeside (www..dk). Billigere priser for annoncører, som vil have faste annoncer i bladet i en længere periode. Billigere priser for annoncører, som leverer artikler til bladet. Artiklerne skal dog være helt uden sammenhæng til annoncørernes produkter. Vi vil forsøge at skabe et bedre samarbejde mellem Oracle- Ekspert og OUGDK. Det kunne feks involvere rabatter til medlemmer af OUGDK. Vi vil forsøge at skabe et bedre samarbejde mellem Oracle- Ekspert og ODTUG. Her kunne være nogle muligheder, efter Deres redaktør er blevet inviteret til at blive Designer-koordinator for ODTUG. Vi vil forsøge at skabe et bedre samarbejde mellem Oracle- Ekspert og Oracle Danmark. Det kunne handle om at få adgang til præsentationer af ny og kommende teknologi fra Oracle. Vi vil se på muligheden for at lave en OracleJob-Børs på www..dk. Vi vil forsøge at etablere muligheder for at kunne bringe anmeldelser af Oracle-relaterede bøger i bladet. Som man nok kan fornemme, bliver det en travl sommer for, og det vil bestemt være i fokus, at dele af arbejdet med bladet kan lægges ud til andre, som har lyst til og mulighed for at deltage i arbejdet. Hvis du kan se, hvordan du kan give en hånd med til at gøre endnu bedre, så skriv endelig en mail om det til Redaktionen@.dk eller brug Kontakt-siden på www.-.dk. Den altid aktuelle Bob Dylan udtrykker situationen på følgende måde på sin seneste plade: They say times are hard, if you don't believe it You can just follow your nose It don't bother me - times are hard everywhere We'll just have to see how it goes - Bob Dylan (spillede i Forum i mandags) Oplag:................250 kopier Udgives af:..............pythia Information...................Kongensvej 3..............2000 Frederiksberg.......................Danmark Telefon:................26279991 Fax:...................26199991 Email:......Info@.dk Web:.......www..dk Ansvarshavende redaktør:.................marc de Oliveira...........Marc@.dk Rettigheder: PYTHIA Information ejer alle rettigheder til indholdet af. Kopiering af bladet i dele eller helhed må kun ske efter skriftligt samtykke fra PYTHIA Information. PYTHIA Information forbeholder sig rettigheder til at offentliggøre og genudgive de trykte artikler, tips mv, samt at tillade bladets læsere at anvende indholdet til såvel personlige som kommercielle formål. PYTHIA Information kan ikke drages til ansvar for eventuelle fejl og mangler i Indholdet af. Artikler mv stilles tilrådighed uden garanti af nogen art. Pris: Enkeltnummer..........DKK 125,00 1 års abonnement.......dkk 600,00 Ved samtidig køb af minimum 5 kopier til samme adresse (enkeltnummer eller abonnement) gives 40% rabat på den samlede pris. Priserne er excl moms. Annoncer: Annoncer til nr 12 skal være PYTHIA Information i hænde senest den 10. maj 2002. Annoncepriser kan findes på: www..dk Password: tesmay

Tid:.......................Torsdag den 25. april kl 9:00-16:00 Sted:................................. Københavnsområdet Pris:...........................................DKK 1500 Early Bird Rabat:...20% ved registrering senest den 1. marts 2002 Abonnentrabat:.............30% for -abonnenter* Tilmelding:....Senest den 12. april 2002 på www..dk * Der gives maksimalt een abonnentrabat pr tegnet abonnement. Abonnentrabatten kan overdrages til andre personer ansat i samme firma som abonnenten. -konferencen er en mulighed for at møde skribenterne af bladets artikler og høre om opdateringer til artiklerne, nye erfaringer, stille spørgsmål og diskutere løsningerne. Der er ingen salgstaler på denne konference. Gå ikke glip af: A F L Y S T En hel dag med -indlæg af forfatterne selv Morgenbrød, frokost, kaffe, te, vand mv Økologisk konference 2002 T-shirt Arrangeret af:

DBATeknisk Artikel TABEL-SAMMENLIGNINGER M. SQL - FEKS. TIL NEM REGRESSIONSTEST AF SYSTEMOPDATERINGER Bodil Feldinger er OCP og ansat som chefkonsulent hos RAMBØLL Informatik A/S. Hun har beskæftiget sig med Oracle s database og udviklingsværktøjer siden 1991. Senest har hun været projektleder/systemarkitekt på en kritisk on-the-fly udskiftning af et større lønsystem parallelt med udvikling og implementering af integrationskomponent til kundens standard time/sags/økonomisystem. Email: baf@ramboll-informatik.dk. Det er ofte interessant at kunne sammenligne indholdet af strukturmæssigt ens tabeller. F.eks til identifikation af forskellene mellem et programs databaseopdateringer før hhv. efter system-ændring (del af regressionstest). Her er en simpel lille værktøjskasse med SQL er, der kan tilpasses og anvendes til sådanne sammenligninger, både til overblik og detaljeret visning af forskelle. Indledning Ved ændring i IT-systemer, der beregner eller opdaterer vigtige data for virksomheden, er det i reglen hensigtsmæssigt at checke, hvilken effekt ændringen har i berørte database-tabeller. Er der sket den/de ændringer i databasen, man forventede og har der eventuelt været sideeffekter? Sammenligning af tabelindhold kan også være interessant i andre sammenhænge. F:eks: hvilke ændringer er der sket i en given tabel (eller flere givne tabeller), siden i morges, hvor jeg tog en kopi af den? I det følgende gennemgås en samling SQL er, der kan tilpasses og udføre arbejdet med at spore ændringer i tabel-indhold både SQL er, der rapporterer detaljeret om forskelle og SQL er, der giver overblik. Eksempel Som gennemgående eksempel vises princippet i en simpel regressionstestprocedure og en samling tilhørende SQL er fra den virkelige verden. Formålet er at checke virkningerne af de løbende opdateringer til et standard Oracle-baseret lønsystem, som et led i en Change Management procedure. Opdateringerne omfatter både kundespecifikke opsætningsændringer og nye versioner af basissystemet. De viste lønberegninger, SQL-scripts, tabelopbygninger og navne er tilrettet/forenklet i denne artikel. Lønsystemet placerer resultatet af sine lønberegninger i en tabel, der hedder BEREGNET_LØN. Den er opbygget således: SQL> desc beregnet_løn Name Null? Type ---------------- -------- ---- MEDA_NR NOT NULL VARCHAR2(5) LØN_ART NOT NULL VARCHAR2(4) TEKST VARCHAR2(30) ANTAL NUMBER(10,2) BELØB NUMBER(10,2) SATS NUMBER(10,2) OPD_DATO NOT NULL DATE OPD_INIT NOT NULL VARCHAR2(30) Hver række i tabellen indeholder en beregnet lønart for en medarbejder. En lønart er f.eks Månedsløn, Askat, Pensionsbidrag o.lign. Den entydige nøgle til tabellen består af MEDA_NR og LØN_ART. Når der foretages opdateringer til lønsystemet, er man interesseret i at vide, hvilke dataændringer dette vil medføre i tabellen BEREGNET_LØN. Procedure for regressionstest af databaseindhold ifm systemændring Programændringer og opsætningsændringer foregår altid på en frisk fuldskala testkopi af produktionssystemet, og rettelserne flyttes først i produktion, når nedenstående procedure er gennemført med tilfredsstillende resultat (se også Figur 1): 1. Foretag lønbereging med den gamle version af program/opsætning (opdaterer tabellen BEREGNET_LØN). 2. Tag en kopi af tabellen BEREGNET_LØN med resultatet af den gamle lønberegning. SQL> CREATE TABLE beregnet_løn_gl AS SELECT * FROM beregnet_løn OBS! Det er vigtigt at huske passende indexer på begge tabeller, ellers bliver sammenligningerne tunge, særligt v. store datamængder. SQL> CREATE INDEX beregnet_løn_gl_ix on beregnet_løn_gl(løn_art, MEDA_NR); 3. Foretag beregninger med det nye/ændrede system (opdaterer i tabellen BEREGNET_LØN). OBS! Det skal sikres, at det datamæssige udgangspunkt her er det samme som det var for punkt 1. 4. Sammenlign de to tabeller BEREGNET_LØN_- GL og BEREGNET_LØN med de SQL-scripts, der er beskrevet i de næste afsnit 5. Gentag punkt 3-4 indtil resultatet er tilfredsstillende... SQL A - detaljeret oversigt over forskelle pr. tabelrække Sammenligning af tabellernes indhold på detaljeret niveau kan foretages helt enkelt, ved at udskrive tabelindholdet for hver tabel i hver sin fil og foretage almindelig filsammenligning mellem disse (f.eks med windiff). Men det kan også gøres som i proceduren her, med SQL, hvor man har større mulighed for at påvirke præsentationen af forskellene. Endvidere er en SQL hurtigere og enklere at eksekvere end proceduren omkring fil-gymnastikken, og giver bedre muligheder for at checke helt specifikke forskelle. Figur 2a viser SQL A, der benyttes ved detaljeret, 4 April 2002

Figur 1. Skitse af procedure for regressionstest af databaseindhold. rækkevis sammenligning af BEREGNET_LØN_GL og BEREGNET_LØN. Ønsker man ikke at få vist rækker, der er ens i de to tabeller, udelades den sidste del-select. Uddrag af sammenligningslisten er vist på Figur 2b. For hver række vises det, om den er udgået, tilføjet eller ændret fra den gamle til den nye version af lønberegningen. Også ens rækker vises i denne version de kan udelades, hvis det forstyrrer overblikket (det gør det som oftest). For ændrede rækker vises den gamle og den ny række lige over hinanden, så det er let at få øje på forskellene. SQL B - detaljeret oversigt over forskelle pr. felt Hvis der er mange kolonner I tabellen, så de ikke alle kan stå på én linie, eller hvis der ønskes særlig fokus på de enkeltfelter, der er ændret, anvendes SQL B, som viser ændringerne pr. kolonne/felt. Se Figur 3a. Uddrag af sammenligningsrapporten er vist på Figur 3b. Her vises én linie pr. ændret felt med hhv gammel og ny værdi for dette. Også nye og udgåede rækker vises her uden værdier, men hvis det ønskes, kan SQL en let tilpasses. SQL C - overblik over forskelle pr. nøgle (pivotering m. CASE ) I mange tilfælde f.eks hvis der er mange data i tabellen, og/eller mange data ændres som følge af en systemændring - vil en mere oversigtsagtig liste være at foretrække i hvert fald indledningsvist. Her er valgt en liste, der kort opsummerer hvilke lønarter, der er påvirket af systemændringen, på hvilken måde, og i hvilket omfang. (Hvis man vil gøre meget ud af det, kan man benytte et dedikeret analyse/pivoteringsværktøj som f.eks Oracle Discoverer eller Microsoft Excel men er behovet blot et hurtigt overblik, og er dynamik ikke essentielt, kan det hurtigst og enklest klares med SQL, som vist i det følgende). Ved at pivotere data her med en CASE-konstruktion - skabes et overblik over de dataændringer, systemændringen har forårsaget. Der pivoteres på SQL A fra Figur 2a. I eksemplet på Figur 4a dannes én række pr. lønart, hvori det vises, hvor mange tabelrækker, der er hhv. ændrede, udgåede, tilføjede og ens mellem den gamle og den ny tabel. (OBS! Hvis man kører en Oracle-version FØR 8.1.6, skal CASE-konstruktionen erstattes af DECODE) Den resulterende udskrift ses på Figur 4b. Pivoteringskonstruktionen kan varieres, afhængig af behov og den aktuelle situation. I nogle tilfælde ønskes der måske et overblik over hvilke typer ændringer, der er sket i lønberegningen pr. medarbejder f.eks hvis der er tale om en systemændring, der kun vedrører få medarbejdere. Ved ændringer af den ydre SELECT i SQL C1 fra figur 4a, kan man således i stedet tælle ændringstyper fordelt på medarbejdernumre. Se SQL C2 i figur 4c. Uddrag af sammenligningsrapporten ses i Figur 4d. Den kan suppleres med ekstra stamoplysninger såsom medarbejdertype i de tilfælde, hvor det vil give en hurtigere eller sikrere verifikation af, om det er de korrekte ændringer, der er sket. SQL D overblik over forskelle feltvis pr. nøgle En anden type oversigt dannes v. pivotering af SQL April 2002 5

-- Find ny version af ændrede rækker SELECT ny.løn_art, ny.tekst, ny.meda_nr, 'Ændret' Aktion, 'Ny' Version, ny.antal, ny.beløb, ny.sats FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.meda_nr=gl.meda_nr AND ny.løn_art=gl.løn_art AND (NVL(ny.antal,0) <> NVL(gl.antal,0) OR NVL(ny.beløb,0) <> NVL(gl.beløb,0) OR NVL(ny.sats,0) <> NVL(gl.sats,0) OR NVL(ny.tekst, ) <> NVL(ny.tekst, )) UNION -- Find gammel version af ændrede rækker SELECT gl.løn_art, gl.tekst, gl.meda_nr, 'Ændret' Aktion, 'Gammel' Version, gl.antal, gl.beløb, gl.sats FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.meda_nr=gl.meda_nr AND ny.løn_art=gl.løn_art AND (NVL(ny.antal,0) <> NVL(gl.antal,0) OR NVL(ny.beløb,0) <> NVL(gl.beløb,0) OR NVL(ny.sats,0) <> NVL(gl.sats,0) OR NVL(ny.tekst, ) <> NVL(ny.tekst, )) UNION -- Find udgåede rækker SELECT gl.løn_art, gl.tekst, gl.meda_nr, 'Udgået' Aktion, 'Gammel' Version, gl.antal, gl.beløb, gl.sats FROM beregnet_løn_gl gl WHERE NOT EXISTS (SELECT 1 FROM beregnet_løn ny WHERE gl.meda_nr=ny.meda_nr AND gl.løn_art=ny.løn_art) UNION -- Find tilkomne rækker SELECT ny.løn_art, ny.tekst, ny.meda_nr, 'Tilføjet' Aktion, 'Ny' Version, ny.antal, ny.beløb, ny.sats FROM beregnet_løn ny WHERE NOT EXISTS (SELECT 1 FROM beregnet_løn_gl gl WHERE gl.meda_nr=ny.meda_nr AND gl.løn_art=ny.løn_art) UNION -- Find rækker, der er uændrede fra gammel til ny version. -- OBS Denne del-select kan evt. udelades af overblikshensyn. SELECT gl.løn_art, gl.tekst, gl.meda_nr, 'Ens' Aktion, 'Gammel' Version, gl.antal, gl.beløb, gl.sats FROM beregnet_løn_gl gl, beregnet_løn ny WHERE gl.meda_nr=ny.meda_nr AND gl.løn_art=ny.løn_art AND NVL(gl.antal,0) = NVL(ny.antal,0) AND NVL(gl.beløb,0) = NVL(ny.beløb,0) AND NVL(gl.sats,0) = NVL(ny.sats,0) AND NVL(gl.tekst, ) = NVL(ny.tekst, ) ORDER BY løn_art,tekst,meda_nr, Aktion Figur 2a. SQL A: Detailliste over ændringer pr. række LØN_ TEKST MEDA_ AKTION VERSIO ANTAL BELØB SATS ---- --------------- ------ -------- ------ --------- --------- --------- 1001 Månedsløn 28183 Ens Gammel 160,33 48328,06... 1001 Månedsløn 28659 Ændret Gammel 160,33 24072,18 1001 Månedsløn 28659 Ændret Ny 160,33 24172,18 1001 Månedsløn 28661 Ændret Gammel 160,33 24483,30 1001 Månedsløn 28661 Ændret Ny 160,33 24583,30 1101 Pensionsbidrag 28362 Tilføjet Ny 6962,3 1101 Pensionsbidrag 28386 Tilføjet Ny 4580,3.. 1901 Særligt løntræk 28645 Udgået Gammel 2,00-123,30 61,65 1901 Særligt løntræk 28646 Udgået Gammel 2,00-123,30 61,65.. 5099 A-skat grundlag 28683 Ændret Gammel 18933,22 5099 A-skat grundlag 28683 Ændret Ny 19033,22 5099 A-skat grundlag 28684 Ændret Gammel 13149,81 5099 A-skat grundlag 28684 Ændret Ny 13249,81 Figur 2b. (Uddrag af) detailliste over ændringer pr. række dannet med SQL A. 6 April 2002

B fra figur 3a. Her tælles, hvor mange forskelle der er pr. kolonne pr. lønart (hvor mange ændringer af f.eks BELØB for de forskellige lønarter). Se SQL D på Figur 5a. Det giver en liste som vist på figur 5b. Der kan varieres mere over disse SQL er, afhængig af, hvad man ønsker at finde/vise til at understøtte verifikation af systemændringen det vil jeg lade op til læserens hittepåsomhed... Tilføjelser til konceptet I gennemgangen af værktøjskassen er noget af funk- -- Find rækker, hvor ANTAL er ændret SELECT ny.løn_art, ny.meda_nr,'ændret ANTAL' Ændring, to_char(gl.antal) GL_VÆRDI, to_char(ny.antal) NY_VÆRDI FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.løn_art=gl.løn_art AND ny.meda_nr=gl.meda_nr AND NVL(ny.antal,0) <> NVL(gl.antal,0) UNION -- Find rækker, hvor BELØB er ændret SELECT ny.løn_art, ny.meda_nr,'ændret BELØB' Ændring, to_char(gl.beløb) GL_VÆRDI, to_char(ny.beløb) NY_VÆRDI FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.løn_art=gl.løn_art AND ny.meda_nr=gl.meda_nr AND NVL(ny.beløb,0) <> NVL(gl.beløb,0) -- Find rækker, hvor SATS er ændret UNION SELECT ny.løn_art, ny.meda_nr,'ændret SATS' Ændring, to_char(gl.sats) GL_VÆRDI, to_char(ny.sats) NY_VÆRDI FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.løn_art=gl.løn_art AND ny.meda_nr=gl.meda_nr AND NVL(ny.sats,0) <> NVL(gl.sats,0) UNION -- Find rækker, hvor TEKST er ændret SELECT ny.løn_art, ny.meda_nr,'ændret TEKST' Ændring, to_char(gl.tekst) GL_VÆRDI, to_char(ny.tekst) NY_VÆRDI FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.løn_art=gl.løn_art AND ny.meda_nr=gl.meda_nr AND NVL(ny.tekst,0) <> NVL(gl.tekst,0) UNION -- Find rækker, der er tilkommet SELECT ny.løn_art, ny.meda_nr,'*ny række*' Ændring, null, null FROM beregnet_løn ny WHERE NOT EXISTS (SELECT 1 FROM beregnet_løn_gl gl WHERE ny.løn_art=gl.løn_art AND ny.meda_nr=gl.meda_nr) UNION -- Find rækker, der er udgået SELECT gl.løn_art, gl.meda_nr,'*udgået række*' Ændring, null, null FROM beregnet_løn_gl gl WHERE NOT EXISTS (SELECT 1 FROM beregnet_løn ny WHERE ny.løn_art=gl.løn_art AND ny.meda_nr=gl.meda_nr) order by løn_art,meda_nr,ændring Figur 3a. SQL B: Detail liste over ændringer pr ændret kolonne/felt LØN_ART MEDA_NR ÆNDRING GL_VÆRDI NY_VÆRDI ------- ------- --------------- --------------- --------------- 1001 28659 Ændret BELØB 24072.18 24172.18 1001 28661 Ændret BELØB 24483.3 24583.30 1001 28664 Ændret BELØB 42572.43 42672.43 1101 28349 *Ny række* 1101 28362 *Ny række* 1101 28386 *Ny række* 1901 28645 *Udgået række* 1901 28646 *Udgået række* 1901 28647 *Udgået række*. 5099 28683 Ændret BELØB 18933.22 19033.22 5099 28684 Ændret BELØB 13149.81 13249.81.. Figur 3b. (Uddrag af) Detail-liste over ændringer pr ændret kolonne. Dannet med SQL B. April 2002 7

tionaliteten skrællet fra, for at hovedidéerne ikke skal drukne i detaljer. Naturligvis kan der kæles mere for detaljerne. Dette afsnit giver nogle eksempler. Visning af forskelle i HTML m. fremhævning af ændrede felter Listen i Figur 2b kan være lidt uoverskuelig er ændringen sket i beløb, antal, tekst eller sats? En måde kunne være at danne differencelisten i HTML I figur 6b ses resultatet, som det kan komme til at se ud i en browser. Der er snydt lidt og vist, hvordan resultatet vil kunne se ud med anvendelse af <TABLE>, <TD> og <TR> tags. Man kan også bygge sin værktøjskasse udelukkende til eksekvering på ias (internet Application Server), såfremt dette findes praktisk/formålstjenligt (f.eks stored procedures, der anvender HTP-pakken). -- I den yderste SELECT pivoteres på aktion feltet, og det tælles, hvor mange -- ændringer, der er pr. lønart. -- Ved ændring af den yderste SELECT, kunne man istedet tælle ændringer pr. medarbejder. SELECT løn_art Lønart, tekst Tekst, SUM(CASE WHEN aktion= Udgået THEN 1 ELSE 0 END) Udgåede, SUM(CASE WHEN aktion= Tilføjet THEN 1 ELSE 0 END) Tilføjede, SUM(CASE WHEN aktion= Ændret and version= Ny THEN 1 ELSE 0 END) Ændrede, SUM(CASE WHEN aktion= Ens THEN 1 ELSE 0 END) Ens FROM ( -- ** Her indsættes SQL A som subselect. ** -- ** -- Find ny version af ændrede rækker SELECT ny.løn_art, ny.tekst, ny.meda_nr, 'Ændret' Aktion, 'Ny' Version, ny.antal, ny.beløb, ny.sats FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.meda_nr=gl.meda_nr AND ny.løn_art=gl.løn_art AND (NVL(ny.antal,0) <> NVL(gl.antal,0) OR NVL(ny.beløb,0) <> NVL(gl.beløb,0) OR NVL(ny.sats,0) <> NVL(gl.sats,0) OR NVL(ny.tekst, ) <> NVL(ny.tekst, )) UNION -- Find gammel version af ændrede rækker SELECT gl.løn_art, gl.tekst, gl.meda_nr, 'Ændret' Aktion, 'Gammel' Version, gl.antal, gl.beløb, gl.sats FROM............ ) -- kun én linie pr lønart skal vises GROUP BY løn_art, tekst -- frasorter lønarter, der er uændrede, fra oversigten HAVING SUM(CASE WHEN aktion= Udgået THEN 1 ELSE 0 END) <> 0 OR SUM(CASE WHEN aktion= Tilføjet THEN 1 ELSE 0 END) <> 0 OR SUM(CASE WHEN aktion= Ændret THEN 1 ELSE 0 END) <> 0 ORDER BY løn_art; Figur 4a. SQL C1. Overblik over ændringer pr. lønart pivotering på SQL A. og fremhæve de ændrede felter med en farve eller lignende. I figur 6a er ideen skitseret i sin enkleste form: det ændrede felt bliver fremhævet med rød farve i den nye version af rækken. For at lette læserens overblik, har jeg i eksemplet undladt indsættelse af <TABLE>, <TD> og <TR> tags, som bør benyttes for at få en pæn præsentation af data. Automatisk generering af sammenligningssql er Har man ofte behov for sammenligningsscripts og til forskellige tabeller kan man med fordel udvikle en procedure eller et script, der genererer sammenlignings-sql erne automatisk ud fra oplysninger i Data Dictionary om tabellerne, disses nøgler og kolonner, således at man ikke behøver skrive SQL erne forfra, Lønart Tekst Udgåede Tilføjede Ændrede Ens ------- --------------- --------- --------- --------- --------- 1001 Månedsløn 0 0 11 133 1101 Pensionsbidrag 0 34 0 0 1901 Særligt løntræk 140 0 0 0 5099 A-skat grundlag 0 0 11 133 Figur 4b. Overblik over ændringer pr. lønart dannet med SQL C1. 8 April 2002

SELECT Meda_nr, MAX(CASE WHEN løn_art=1001 and aktion in ( Ændret') and Version='Ny' THEN 'x' ELSE null END) "Ændr. 1001", MAX(CASE WHEN løn_art=1101 and aktion in ('Tilføjet') THEN 'x' ELSE null END) "Ny 1101", MAX(CASE WHEN løn_art=1901 and aktion in ('Udgået') THEN 'x' ELSE null END) "Udgået 1901", MAX(CASE WHEN løn_art=5099 and aktion in ('Ændret') and Version='Ny' THEN 'x' ELSE null END) "Ændr 5099" FROM (--** Indsæt subselect identisk med SQL A. **... ) GROUP BY Meda_Nr HAVING SUM(CASE WHEN aktion= Udgået THEN 1 ELSE 0 END) <> 0 OR SUM(CASE WHEN aktion= Tilføjet THEN 1 ELSE 0 END) <> 0 OR SUM(CASE WHEN aktion= Ændret THEN 1 ELSE 0 END) <> 0 ORDER BY Meda_Nr Figur 4c. SQL C2. Overblik over ændringstyper pr. medarbejder pivotering på SQL A. hver gang man skal køre sammenligning på en ny tabel. Altså et sammenligningsscript-kodegenereringsprogram In-line functions til forenkling af kode. Når man ser på koden i SQL erne i f.eks. SQL A og til proceduren: f.eks TabelNavn1, RowId1, TabelNavn2, RowId2. Også andre dele af koden kan generaliseres v. brug af inline functions og kombineres med autogenerering af kode. MEDA_NR Ændr. 1001 Ny 1101 Udgået 1901 Ændr 5099 ------- ----------- -------- ------------ ------------- 28183 x x 28191 x x 28192 x... 28414 x... 28579 x 28580 x x 28582 x... 28659 x x x 28661 x x x... Figur 4d. Overblik over ændringstyper pr. medarbejder dannet med SQL C2. SQL B kan man se, at koden til at bestemme, om 2 rækker er ens, gentages mange gange. Det er oplagt at kode en in-line-function til bestemmelse af dette, a la koden vist i Figur 7a. Første del-select i SQL A vil da se ud, som vist på figur 7b. De øvrige del-selects kodes på tilsvarende vis. Vil man udnytte mulighederne fuldt ud, koder man en helt generel ENS_RÆKKER-function, som kan sammenligne hvilkesomhelst 2 rækker i hvilkesomhelst 2 tabeller, ved at læse tabel-oplysninger i Data Dictionary og danne dynamsik SQL. Input-parametre Brug af in-line functions performer ikke helt så effektivt som de viste SQL A og SQL B. Til gengæld gør den koden enklere og mere læsbar og formindsker risikoen for kodefejl. Bruger man den dynamiske version er der yderligere den fordel, at den fortsat fungerer, når databasestrukturen ændres. Supplerende oplysninger på sammenligningsrapporter Man vil ofte have behov for supplerende oplysninger i rapporterne i det gennemgående eksempel måske oplysninger om medarbejdertype, om medarbejderen er nyansat/fratrådt el. lign. Disse oplysninger kan SELECT løn_art Lønart, sum(case when ÆNDRETFELT='Ændret BELØB' then 1 else 0 end) Ændret Beløb, sum(case when ÆNDRETFELT='Ændret ANTAL' then 1 else 0 end) Ændret Antal, sum(case when ÆNDRETFELT='Ændret SATS' then 1 else 0 end) Ændret Sats, sum(case when ÆNDRETFELT='Ændret TEKST' then 1 else 0 end) Ændret Tekst, sum(case when ÆNDRETFELT='*Ny række*' then 1 else 0 end) Ny Række, sum(case when ÆNDRETFELT='*Udgået række*' then 1 else 0 end) Udgået Række FROM ( -- Indsæt SQL B. ) GROUP BY Løn_art ORDER BY Løn_art Figur 5a. SQL D. Oversigt over forskelle, feltvis pr. key. Pivotering på SQL B. April 2002 9

Lønart Ændret Beløb Ændret Antal Ændret Sats Ændret Tekst Ny Række Udgået Række ------ ------------ ------------ ----------- ------------ --------- ------------- 1001 11 0 0 0 0 0 1101 0 0 0 0 34 0 1901 0 0 0 0 0 140 5099 11 0 0 0 0 0 Figur 5b. Liste over ændringer pr. kolonne dannet med SQL D. joines ind fra andre tabeller (her medarbejder-stamtabel) og der kan pivoteres på oplysningerne, på linie med oplysninger fra basis-tabellen. som beskrevet i denne artikel, er en god og billig måde at teste og verificere ændringerne på, og kan iværksættes hurtigt. Den maskinelle sammenligning kan muliggøre etablering af en systematisk changemanagement-procedure, selvom opgavens størrelse og/eller tid og økonomi ikke retfærdiggør et egentligt, prompt <html><body><font face="courier New" size=1> -- Find ny version af ændrede rækker SELECT ny.løn_art, ny.tekst, ny.meda_nr,'ny' Version, (case when ny.antal = gl.antal then to_char(ny.antal) else '<font color="red">' to_char(ny.antal) '</font>' end), (case when ny.beløb = gl.beløb then to_char(ny.beløb) else '<font color="red">' to_char(ny.beløb) '</font>' end), (case when ny.sats = gl.sats then to_char(ny.sats) else '<font color="red">' to_char(ny.sats) '</font>' end) '<br>' FROM beregnet_løn ny, beregnet_løn_gl gl WHERE...... UNION -- Find gammel version af ændrede rækker SELECT gl.løn_art, gl.tekst, gl.meda_nr, 'Gammel' Version, to_char(gl.antal), to_char(gl.beløb), to_char(gl.sats) '<br>' FROM......... ORDER BY løn_art,meda_nr,version; prompt </font></body></html> Figur 6a SQL A(html) Fremhævelse af ændringer med rød farve i HTML (udvidelse af SQL A med HTML-tags). Test-processen på skinner Hvis man benytter den beskrevne procedure til verifikation af løbende ændringer til et standardsystem og/eller hvis virksomheden ikke har daglig adgang til SQL-kompetance på det niveau, der skal til, for at arbejde direkte med SQL'erne, er det nærliggende at Oversigt over ændrede rækker i BEREGNET_LØN 1000 Månedsløn 28659 Gammel 160,33 24072,18 1001 Månedsløn 28659 Ny 160,33 24172,18... 5099 A-skat grundlag 28659 Gammel 24072,18 5099 A-skat grundlag 28659 Ny 24172,18 5099 A-skat grundlag 28661 Gammel 24483,30 5099 A-skat grundlag 28661 Ny 24583,30... Figur 6b. Forskelle er fremhævet med rød farve v.hj.a HTML. (Tabel-tags anvendt udover tags i SQL A(html)) sætte hele processen så meget på skinner, at den kan gennemføres af en (super)bruger - f.eks. via en browser-grænseflade. (Super)brugeren udstyres med en arbejdsprocedurebeskrivelse og adgang til en samling sammenligningsscripts - pænt pakket ind i en brugervenlig grænseflade. Hvor avanceret løsningen skal være, må afhænge af behov, brugererfaring og ambitionsniveau. Et avanceret analyse-værktøj som Discoverer er smart og giver mange muligheder - men vil nok omvendt være 'overkill' i en del tilfælde. Afsluttende bemærkninger SQL-baseret maskinel sammenligning af tabeldata før og efter en program- eller opsætningsændring færdigkøbt, automatiseret test-system. Metoden er en sikker og enkel måde at foretage regressionstest på, når man blot skal teste lagrede data og ikke brugerfladens opførsel eller komponenters indbyrdes interaktion. Der findes masser af programmer, der påvirker mere end én tabel (det gør eksemplets lønsystem også i virkeligheden), og som gør det på mere subtil vis end i det gennemgående eksempel - og så må man lave SQL-sammenligningsscripts for hver af de berørte tabeller. Eventuelt, som foreslået, ved at generere sammenligningsscripts dynamisk. Sammenligning af strukturmæssigt ens tabellers indhold kan være interessant i andre sammenhænge end ved test af system-ændringer. Metoden kan f.eks også bruges til undersøgelse af, hvilke data-opdateringer, der er sket i en database siden sidste full- 10 April 2002

CREATE OR REPLACE PACKAGE ENSPACK IS FUNCTION Ens_Beregnet_Løn (prowidny rowid, prowidgl rowid) RETURN VARCHAR2; -- Nødvendig pragma-besværgelse, for at funktionen kan benyttes som in-line -- function (direkte fra SQL). Den lover at funktionen ikke har side-effekter. PRAGMA RESTRICT_REFERENCES (Ens_Beregnet_Løn, WNDS, WNPS); END ENSPACK; CREATE OR REPLACE PACKAGE BODY ENSPACK IS FUNCTION Ens_Beregnet_Løn (prowidny rowid, prowidgl rowid) RETURN VARCHAR2 IS Resultat NUMBER(1); BEGIN SELECT 1 INTO Resultat FROM Beregnet_løn_gl gl, Beregnet_løn ny WHERE gl.rowid=prowidgl AND ny.rowid=prowidny AND gl.meda_nr=ny.meda_nr AND gl.løn_art=ny.løn_art AND NVL(gl.antal,0) = NVL(ny.antal,0) AND NVL(gl.beløb,0) = NVL(ny.beløb,0) AND NVL(gl.sats,0) = NVL(ny.sats,0) AND NVL(gl.tekst,' ') = NVL(ny.tekst,' '); RETURN J ; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN N ; END; END ENSPACK; Figur 7a. Function til bestemmelse af, om række i Beregnet_løn er identisk m. række i Beregnet_løn_gl copy af denne (eller af en enkelt tabel i basen). Sådanne undersøgelser kan også klares på anden vis (logning, auditing etc.) men det kræver, at man på forhånd har taget højde for, og brugt tid på, behovet. God fornøjelse med metoden. Udover at være en nem, hurtig, billig og sikker måde at teste på, er det også sjov SQL-gymnastik!! [Kodestumperne kan hentes på vores hjemmeside www..dk, Red.] -- Find ny version af ændrede rækker SELECT ny.løn_art, ny.tekst, ny.meda_nr, 'Ændret' Aktion, 'Ny' Version, ny.antal, ny.beløb, ny.sats FROM beregnet_løn ny, beregnet_løn_gl gl WHERE ny.meda_nr=gl.meda_nr AND ny.løn_art=gl.løn_art AND EnsPack.Ens_Beregnet_Løn(ny.RowId, gl.rowid) = 'N'; Figur 7b. Forenklet del-select fra SQL A, ved brug af in-line-function Helsidesannoncer i DKK 6.000 (excl moms) Læs mere på: www..dk April 2002 11

Fax eller send kuponen til: Pythia Information Kongensvej 3 2000 Frederiksberg Fax: 26199991 Eller bestil via vores hjemmeside: ABONNEMENT Ja tak, jeg ønsker: http://www..dk Eller e-mail nedenstående oplysninger til: salg@.dk nr 1 - DKK 125,00 pr stk...dkk nr 2 - DKK 125,00 pr stk...dkk nr 3 - DKK 125,00 pr stk...dkk nr 4 - DKK 125,00 pr stk...dkk nr 5 - DKK 125,00 pr stk...dkk nr 6 - DKK 125,00 pr stk...dkk nr 7 - DKK 125,00 pr stk...dkk nr 8 - DKK 125,00 pr stk...dkk nr 9 - DKK 125,00 pr stk...dkk nr 10 - DKK 125,00 pr stk...dkk nr 11 - DKK 125,00 pr stk...dkk nr 12 - DKK 125,00 pr stk...dkk 2000 - DKK 300,00 pr stk (3 numre)...dkk 2001 - DKK 600,00 pr stk (6 numre)...dkk 2002 - DKK 600,00 pr stk (6 numre)...dkk 1 års abonnement (6 numre) - DKK 600,00 pr stk...dkk Jeg vil gerne begynde abonnementet med nr: - 40% ved samtidig køb af minimum 5 ens blade eller abonnementer...dkk - 45% ved samtidig køb af minimum 10 ens blade eller abonnementer...dkk - 50% ved samtidig køb af minimum 20 ens blade eller abonnementer...dkk - 60% ved samtidig køb af minimum 50 ens blade eller abonnementer...dkk Bemærk: Når abonnementerne købes med mængderabat bliver alle blade sendt i én samlet forsendelse. Det er altså ikke muligt at få bladene sendt til individuelle modtagere. Pris i alt Firma:...DKK Priserne er excl moms. Navn: Adresse: Postnr/By: Land: E-mail: Refereret af: Adresse: Postnr/By: Ved hver tegning af minimum 1 års abonnement på sender vi et stk signeret kvalitetskopi af Oraclerne til den læser, som har refereret den nye abonnent til os. Kopien er trykt på kraftigt papir. Den er nummereret og signeret med rød tush. En -læser er en person med eget abonnement eller en person ansat i et firma, som har tegnet abonnement på. Man kan ikke referere et abonnement til sig selv. Som referencegave ønskes en signeret kopi af Oraclerne fra : Nr 1 Det er fordi de ikke kan li fremmednøgler... [ ] Nr 2 Det er jo også vores Designer mand... [ ] Nr 3 Har du prøvet at skifte din Where-clause ud med en Santa clause?... [ ] Nr 4 Pro*C... [ ] Nr 5 No more table space...[ ] Nr 6 Komme i Forms...[ ] Nr 7 Rodeo...[ ] Nr 8 Joins...[ ] Nr 9 Triggere...[ ] Nr 10 6i...[ ] Nr 11 KageForms...[ ] 12 April 2002

UPDATE THE DESIGNER REPOSITORY THROUGH THE WEB Marc de Oliveira is IT Team Leader at NNE. He has a M. Sc. degree in Computer Science from the University of Copenhagen. Marc is writer and presenter and has worked with Oracle tools since 1989 (primarely with CASE/Designer). E- mail: Marc@deOliveira.dk. Introduction The Designer Repository is a very good location for keeping structured information about your business's application systems and their related elements like requirements, time plans, status information and other types of application system related information. Much of this information is naturally supplied by developers who use Designer for designing and developing the application systems but some things are best maintained by managers, end users, customers and others who do not know how to use Designer. It might be of great value to offer a simple way to view and maintain some specific parts of the Designer repository to these non-developers. An obvious choice for making such an interface would be to generate WSG modules based on the repository views that show the objects relevant to specific project management tasks. This solution is perfect for showing repository data but it does not allow for making inserts and updates to the repository tables because WSG modules based on views will not allow updates.. It is possible to trick Designer to generate table APIs for the repository views so that updatable modules can be generated but even this will not generate modules that will update the repository in the supported way. The modules must go through workareas and use the Designer API for updates to be supported by Oracle. And that brings us to the point of this paper. It will show you how to develop WSG modules that will allow for viewing, updating, inserting and deleting elements from the Designer Repository using the supported Designer API. I presume that you already know something about the Designer API and how to build and generate WSG modules. If you have never made a Designer API program or have never generated a WSG module you will have to look up some of the things I mention elsewhere. As an example I will show how to generate a WSG module for inserting, updating and deleting Application Systems in the Designer Repository. The same method can be used to generate WSG modules for all other element types. There are a couple of differences between generating application systems and other elements that I shall mention throughout the paper. The paper is divided into the following five sections that describe the different steps you must take to make your own updateable web interface to the Designer Repository: Setup the workarea through the web Define a general Web module for managing multi line text Setup the table APIs Define the WSG modules for managing Designer elements Join the modules together The first and second step only need to be performed once while the last three steps must be done for each Designer element that you want to be able to maintain through your WSG application. Setup the workarea through the web When manipulating the Designer API it is necessary to do it through a workarea. As people will usually want to choose one workarea for performing a series of task I suggest that you let the users select a workarea using a WSG module and store it in a table from where it is referenced in the following manipulating tasks until a new workarea is chosen. Create a table for storing the chosen workarea The table should contain one column for defining the user and one for defining the chosen workarea name. This could be done with two columns but I suggest that you make the table a little more generic by adding a column that describe the type of context value to be stored (like Workareas). It could look something like this: CREATE TABLE CHOSEN_CONTEXT ( ORACLE_USER varchar2(30) default USER, CONTEXT_TYPE varchar2(30) default WORKAREA, CONTEXT_VALUE varchar2(30)); Then create a primary key containing the columns ORACLE_USER and CONTEXT_TYPE. And also create a foreign key to the CI_WORKAR- EAS view like this: CHOSEN_CONTEXT.CONTEXT_VALUE -> CI_WORKAREAS.NAME Make sure that the foreign key is only validated on the client so that the CONTEXT_VALUE column can be used for other types of context values. This will be necessary for setting the context application. This is described in the section called Setting the context. Create a WSG module for selecting the workarea Create a WSG module with a module component based on CHOSEN_CONTEXT with the following where clause: ORACLE_USER = USER and CONTEXT_TYPE = WORKAREA Then include CI_WORKAREAS as a lookup table. Designer Teknisk Artikel April 2002 13

Create a function for getting a user s chosen workarea In the following you will need a simple function that returns the chosen workarea of the current user. It could look like this: create function get_chosen_context( p_user in varchar2, p_context_type in varchar2) return varchar2 is v_chosen_context varchar2; begin select context_value into v_chosen_context from chosen_context where oracle_user = p_user and context_type = p_context_type; return v_chosen_context; exception when no_data_found then raise_application_error(-20000, No p_context_type is chosen ); end; Define a general Web module for managing multi line text There is a special undocumented (but still supported) API for manipulating multi line text in the Designer Repository. In the following section I shall describe how to make a simple generic module that will let you update any multi line text of any object in the Designer Repository. This generic module will subsequently be linked into each Designer element manipulation WSG module (the linking will be described in a later section). There are only two steps for building a pl/sql module that can manipulate multi line texts: Create pl/sql procedure for editing multi line text Build a procedure for updating multi line text of the Designer Repository Create pl/sql procedure for editing multi line text First we need to build a pl/sql procedure that will allow people to view and edit the multi line text stored in the Designer Repository from within a web browser. The procedure should be generic so that it can display the text of any Designer element. Therefore the procedure must have the following parameters: create procedure edit_multi line ( -- The type of text to be edited p_txt_type in varchar2 -- The IRID of the Designer element p_txt_ref in number -- The type of element p_element_type_for in varchar2 ) is These are the parameters needed to manipulate the multi line text of a specific Designer element wthin the CDI_TEXT table. To send out the HTML content to build a web page you can use the htp.print procedure of the HTP package. The only two visible items that this web page need to contain is an editable multi line textarea and a button for submitting the changes. The content of the textarea is fetched from the CDI_TEXT table usingt the above parameters. To send the updated text back to the repository you must place the textarea item and the parameters inside an HTML Form and have the Submit item post them as parameters to a pl/sql procedure that will update the repository. All the parameters of the edit_multi line procedure can be included in the Form as hidden items so that they are not seen by the user. All this is fairly simple to do in pl/sql. It will look something like this: htp.print('<html><head>'); htp.print('</head><body>'); htp.print( '<form action="update_multi line" method="post">'); htp.print('<input type=hidden name=p_txt_ref value=' p_txt_ref '>'); htp.print('<input type=hidden name=p_txt_type value=' p_txt_type '>'); htp.print('<input type=hidden name=p_element_type_for value=' p_element_type_for '>'); htp.print( '<textarea cols=90 rows=25 name=p_text>'); -- Set the current workarea jr_context.set_workarea( get_chosen_context(user, WORKAREA )); -- Fetch text from CDI_TEXT FOR t IN (select txt_text from cdi_text where txt_ref = p_txt_ref and txt_type = p_txt_type order by txt_seq) LOOP htp.print(t.txt_text); END LOOP; htp.print('</textarea>'); htp.print('<p><input type=submit value=commit>'); htp.print('</form>'); htp.print('</body></html>'); Build a procedure for updating multi line text of the repository The procedure called from the above Submit event must have the parameters matching the submitted form: create or replace procedure update_multi line ( p_txt_ref in number, p_txt_type in varchar2, p_element_type_for in varchar2, p_text in varchar2) is The Designer API for updating the multi line text is fairly simple. After the update has completed, call the edit_multi line procedure again to requery the text and display it for the user. 14 April 2002

v_bufwrt integer; begin -- Set the current workarea jr_context.set_workarea( get_chosen_context(user, WORKAREA )); -- Open an activity cdapi.open_activity; -- Update the multi line text rmotextp.writeall( P_TXT_REF, P_TXT_TYPE, p_element_type_for, P_TEXT, length(p_text), v_bufwrt); -- This is a procedure that validates -- and closes the activity -- See Repository API doumentation for -- the content of this function validate_and_close_ativity; -- Redisplay the updated text htp.print('<h1><font color="#00ff00"> Success</font></h1><p>Text updated'); edit_multi line( p_txt_ref, p_txt_type, p_element_type_for); exception when others then raise_application_error(-20000, 'Update_multi line: ' sqlerrm); end; The web module will look like this when executed: Setup the table API To be able to generate updatable WSG modules you will need table APIs for the API views to be used. Because Designer will not generate a table API for a view, you must have the repository view (like ci_application_systems) represented as a table in Designer. These are the steps necessary to retrofit a repository view into Designer as a table. After that I shall show how you must add some API evets to the table before you generate its table API. Create a table as a copy of the repository view Connect to the repository in sql*plus and create a table (X) like this: create table X as select * from ci_application_systems where 1=2; This example shows how to create a table as a copy of ci_application_systems. Just replace the view name with the name of the view you will need to retrofit. The where clause 1=2 will assure that no data is copied into the created table. Retrofit the table into Designer In the Design Editor choose the menu: Generate -> Capture design of -> Server model and retrofit the table X into the Designer Repository After the table is retrofitted into the repository rename the table X to the corresponding views name (like ci_application_systems). Then add a primary key to the table composed by the column IRID. Without a primary key Designer will not generate the table API. It is a good idea to set the properties like Prompt, Display, Context, Display Sequence, Display Length and Display Height on the retrofitted table. This will make the step of making modules on these tables easier. Add three table API triggers Add the following three table API triggers to the retrofitted table: Pre-Insert, Pre- Update and Pre-Delete, all with the following code: begin return; end; This will ensure that the API will not make inserts, updates and deletes to the repository. Generate the table API Finally, generate the table API into the database. The API package will install without errors while the generated triggers will fail because views cannot have triggers assigned to them but that is ok. We only need the main cg$ci_application_system package for building WSG modules. Define the WSG modules for managing Designer elements The next step is to build the WSG modules for manipulating the Designer elements. In this example I shall show how to build a WSG module for manipulating Application Systems. April 2002 15

Create a WSG module based on the retrofitted table I described how the repository views were retrofitted into the Designer Repository as tables in the last section. Now you need to create a module with a module component based on the retrofitted table (which is actually a view) and allow insert, update and delete to the module component and the bound items that you want to be able to manipulate. It is a good idea to name the modules and module components in a consistent way where the only difference in the name is the element type as it is defined in the txt_element_type_for column of cdi_text (that would be APP for Application Systems, E1 for Objectives etc). This will make it possible to define return links on the edit_multi line web module that will link back to the View Form of the current Designer element. The WSG module for maintaining Application Systems could be called DESIGN_APP while the module for maintaining Objectives could be called DESIGN_E1. Their module components could simply be called APP and E1. Add a Pre-Query event All module components that are based on a repository view must have a Pre-Query event that sets the chosen workarea of the current user to avoid that multiple versions of the same element is displayed in the module: begin -- Set the current workarea jr_context.set_workarea( get_chosen_context(user, WORKAREA )); end; Add a Pre-Insert event To insert a new object in the repository you must use the supported Repository API. This can be done in the Pre-Insert event of the module component. To insert a new Application System into the repository you must do the following: declare -- Designer API structure for holding -- the application system record v_appl cioapplication_system.data; begin -- Set the current workarea jr_context.set_workarea( get_chosen_context(user, WORKAREA )); -- Open an activity cdapi.open_activity; -- Set the values of the columns v_appl.v.name:= form_val.name; v_appl.i.name:= TRUE; v_appl.v.container_subtype:= 'APP'; v_appl.i.container_subtype:= TRUE; v_appl.v.authority:= form_val.authority; if v_appl.v.authority is not null then v_appl.i.authority:= TRUE; end if; v_appl.v.display_title:= form_val.display_title; if v_appl.v.display_title is not null then v_appl.i.display_title:= TRUE; end if; -- Insert the application system cioapplication_system.ins(null,v_appl); -- Select the inserted row to know the -- IRID that it was given cioapplication_system.sel(v_appl.v.irid,v_appl); -- Set CURR_VAL to make the following -- Update screen select the correct row. curr_val.irid:= v_appl.v.irid; -- Close the activity validate_and_close_ativity; -- Return TRUE to make the module -- continue with its own insert procedures RETURN TRUE; end; Note that for all other element types than Application Systems you will have to set the context Application System with the cdapi.initialize(<appsys name>) procedure. This is discussed in section Setting the context. Add a Pre-Update event To update an object in the repository you must use the supported Repository API. This can be done in the Pre-Update event of the module component. To update an Application System you must do the following: declare -- Designer API structure for holding -- the application system record v_appl cioapplication_system.data; begin -- Set the users chosen workarea jr_context.set_workarea( get_chosen_context(user, WORKAREA )); -- Open an activity cdapi.open_activity; -- Select the current record of the -- module into the API data object cioapplication_system.sel( form_val.irid, v_appl); -- Update the columns that have been -- changed in the view form if form_val.name!= v_appl.v.name then v_appl.v.name:= form_val.name; v_appl.i.name:= TRUE; end if; if form_val.remark!= v_appl.v.remark then v_appl.v.remark:= form_val.remark; v_appl.i.remark:= TRUE; end if; -- Update the repository object cioapplication_system.upd( v_appl.v.irid,v_appl); -- Close the activity validate_and_close_ativity; -- Return TRUE to make the module -- continue with its update procedures RETURN TRUE; end; Add a Pre-Delete event To delete an object from the repository you must use the supported Repository API. This can be done in the Pre-Insert event of the module component. To delete an Application System you do the following: 16 April 2002

declare -- Designer API structure for holding -- the application system record v_appl cioapplication_system.data; begin -- Set the current workarea jr_context.set_workarea( get_chosen_context(user, WORKAREA )); -- Open an activity cdapi.open_activity; -- Delete the object cioapplication_system.del(form_val.irid); -- Close the activity validate_and_close_ativity; -- Return TRUE to make the module -- continue with its delete procedures RETURN TRUE; end; Create links to the multi line text module Each multi line text type (like Description, Note, User help text etc) of the current Designer element type creates an unbound item of type SQL Expression with the name UPDATE_DESCRIPTION or UPDATE_- NOTE etc. The Display property must be Yes. The Derivation expression must build a link to the QueryViewByKey procedure of the multi line text manipulation module. To do this the Derivation Expression of the unbound item for updating the Description of an Application System should be something like this: '<a href="edit_multi line?p_txt_type=cdid- SC' '&P_TXT_REF=' irid '&P_ELEMENT_TYPE_FOR=' container_subtype '">Click here to update text</a>' For element types other than Application Systems the "container_subtype" should be replaced with "element_type_name". You could also have a function return the actual text from the CDI_TEXT table together with the link. "CDIDSC" should be replaced by the appropriate text type ("CDINOT" for notes, "CDHELP" for user help etc). It is possible to see the text type code by selecting the multi line text type in the RON (do not open the text editor) and pressing the F5 key (it is shown under View column). Generate the WSG module Now you can generate the module and have the standard buttons for insert, update and delete use the supported Designer API for manipulating the repository tables. The module will act like any other WSG module but will use the Designer API rather than inserting, updating and deleting directly into the repository tables. The module for maintaining the Application Systems will then look something like the following figures (the figure on this page shows the record list and the one on the next page shows the view form). Join the modules together When you have built a number of WSG modules for maintaining the different parts of the Designer Repository they should be linked together in a struc- April 2002 17

tured way reflecting the structure of Designer. Ie the main application should display the Application Systems (that would be the WSG module that we just built in the previous section). Joining Masters and Details If you want to join in a module for managing Objectives you will have to do some tricks because Application Systems and Objectives are no longer related as Master and Detail as they were in previous versions of Designer. In Designer 6i and 9i the relationships between Application Systems and primary elements goes through the intersection table SDD_FOLDER_MEMBERS. To link Application Systems with Objectives you must add a detail module component to the DESIGN_APP module based on SDD_FOLDER_MEMBERS with a lookup to CI_OBJECTIVES (with the Outer Join property set to No to restrict the SDD_FOLDER_MEM- BERS to only the Objectives. Place both module components in the same window to have the details (Objectives) placed below the master (Application System). Include the IRID and Name item from the CI_OBJECTIVES view in the detail module component. Set the Context property of Name to Yes to have it displayed in the record list. Set the Display property of IRID to No and change its Name property to OBJ_IRID. Then add an unbound item in the detail module component to form the link to the WSG module for maintaining the Objectives. The Derivation Expression of that unbound item should look like this: 18 April 2002

'<a href= "design_e1$e1.queryviewbykey? P_IRID=' OBJ_IRID '">Edit</a>' This link will go directly to the View Form of the DESIGN_E1 WSG module where it is possible to update and delete the selected Objective and insert a new Objective. If no Objectives exist on the selected Application System there will be no links to create the first Objective. Therefore you must add a link to the main DESIGN_E1 module in the Top Of Record List User Help Text, like this: <a href="design_e1$.startup">set Objectives</a> The DESIGN_APP module should then look something like the diagram to the right. Setting the context There is one other problem when building the Master Detail relationship in this way. When there is no real Master-Detail relationship between Application Systems and Objectives the information about the Application System is not stored with the Objectives record. Instead the context application system is defined with the cdapi.initialize(<appsys name>) procedure just before opening an activity. The problem is solved in the same way that the context workarea is managed. Create a Post-Query event on the APP module component of the DESIGN_APP module and have it store FORM_VAL.NAME in the CHOSEN_CONTEXT table described earlier, like this: begin update CHOSEN_CONTEXT set CHOSEN_VALUE:= FORM_VAL.NAME where ORACLE_USER = USER and CONTEXT_TYPE = APPLICATION ; exception when no_data_found then insert into CHOSEN_CONTEXT values (USER, APPLICATION,form_val.name); end ; Then it is easy to set the context application using the previously defined function for fetching the current workarea, like this : cdapi.initialize( get_chosen_context(user,'application')); cdapi.open_activity; Conclusion The principles described in this paper will allow you to build your own updateable web interface to the Designer Repository where you can choose which elements can be manipulated and add your own business logic to how each element should behave. If you combine this with the User Extensibility features of Designer there are no limits as to what the Designer Repository could be used for. I have focused on adding project management features that will support our development and documentation strategy by storing and presenting requirements, time plans, resource allocations etc in the repository. What will you use it for? [The code examples can be downloaded from our home page www..dk, Red.] April 2002 19

PL/SQL Teknisk Artikel AFVIKLINGSPLAN OG TRÆSØGNING Af Martin Jensen - Oracle Consulting. Martin har siden 1982 arbejdet med bl.a. Oracle s database-kerne, samt med forskellige aspekter af objektorienteret systemdesign. Kender du det? Man sidder og kigger på databasesystemets tungeste SQL-sætninger i et eller andet værktøj, og får lyst til at se hvorledes database-systemets afviklingsplan for sætningen egentligt ser ud. Man trækker så sætningen over i en SQL*Plus session, og prøver med passende bind-variable at bede om en explain plan. Men her kører sætningen hurtigt? Der kan jo være mange årsaget til at det ikke altid er let at genetablere den afviklingsplan en given SQLsætninger er kørt med til en tuning-session. Måske er diverse sessionsparametre anderledes (nls_sort, optimizer_mode,...) eller også kører sætningen som en anden bruger, eller måske ændres planen når der fremfor bind-variable anvendes konstanter. Det er derfor meget glædeligt i version 9i at finde det nye view v$sql_plan, der rummer afviklingsplanerne på de senest parsede SQL-sætninger. Ser man nærmere på dette nye view opdager man at man ved at holde address, hash_value og child_number fast, får en samling rækker der rummer planen for en afvikling af en SQL-sætning. De enkelte rækker hænger sammen ved at starte med parent_id null, og så følge denne top s id til andre rækkers parent_id til hele træet er samlet: select address, hash_value, child_number, nvl( to_char( cost ), 'n/a' ) ' ' nvl(to_char(cardinality),'n/a') cost, rpad( ' ',2*level ) operation decode(options,null,null,'(' options ')') decode(object_owner,null,null,object_owner '.' object_name ) operation from v$sql_plan start with parent_id is null connect by prior id = parent_id and prior address = address and prior hash_value = hash_value and prior child_number = child_number: Attributten position angiver i hvilken rækkefølge noderne på samme niveau skal stå, hvilket kan klares med denne nye order by syntaks for træ-søgninger: order siblings by position; Værdierne til address, hash_value og child_number kan fås fra viewet v$sql, der i attributten sql_text rummer de 1000 første tegn af den pågældende SQLsætning. Så ved at udnytte at vi i 9i nu også kan anvende træsægninger i forbindelse med joins, kan vi finde en given SQL-sætning og dens plan således: (her leder vi efter sætninger, som indenfor de første 1000 tegn anvender ordet siblings: select s.sql_text, p.cost, p.operation from v$sql s, ( select address, hash_value, child_number, parent_id, id, position, nvl(to_char(cost),'n/a' ) ' ' nvl(to_char(cardinality),'n/a') cost, rpad( ' ', 2*level ) operation decode( options, null, null,' (' options ') ' ) decode(object_owner,null,null, object_owner '.' object_name ) operation from v$sql_plan start with parent_id is null connect by prior id = parent_id and prior address = address and prior hash_value = hash_value and prior child_number = child_number order siblings by position ) p where p.address = s.address and p.hash_value = s.hash_value and p.child_number = s.child_number and s.sql_text like '%siblings%'; Ved fra SQL*Plus at anvende følgende kommandoer kommer selve SQL-sætningen kun ud for første række i træet: column operation format a60 column cost format a15 break on sql_text nodup Anvend eventuelt v$sqltext fremfor v$sql hvis sætningerne er på mere end 1000 tegn, til gengæld må man så se på flere mulig afviklingsplaner for samme sætning, da child_number ikke er med i v$sqltext. Nu er der jo ingen sikkerhed for at informationerne i v$sql_plan og v$sql bliver liggende længe nok til at man kan nå at få fat i dem, men hvis man på forhånd ved at alle selects mod en tabel eller et view er af interesse, kunne man oprette en fine grained audit politik mod tabellen eller view et med en module_handler, der først kandt den egentlige sætning i dba_fga_audit_trail, og derefter kopierede de relevante rækker fra v$sql_plan over til en tabel man i rå og mag komme studere. Gode tips, som trykkes i belønnes med et økologisk hjerte af marcipan overtrukket med chokolade (240 g) 20 April 2002