Manglende konsistens i datamodellen og upræcise SQLsætninger er årsagen til, at mange IT-systemer fejler. Af Seniorkonsulent Carsten Saastamoinen-Jakobsen Skal datamodellen blot være på 3NF (normalform)? Er det at finde foreningsmængden mellem 2 tabeller blot at anvende UNION-operatoren? Nej og Nej! Hvis et system skal være stabilt og konsistent hen over tid er 3NF ikke nok. Hvis kravet til datamodellen er 3NF er UNION-operatoren ikke anvendelig i alle situationer. Hvorfor oplever mange udviklere, at deres fejlfrie program efter at have kørt problemfrit i uger, måneder eller måske endda år pludselig fejler. Grunden vil i mange tilfælde være et for dårligt forarbejde. Min kære dansklærer fra gymnasiet, havde den holdning, at mindst den første time af en fire timers eksamen i dansk stil skulle bruges til at foreberede, hvad der skulle blive til en velstruktureret stil i de sidste knap tre timer. Hvis forholdet ikke var 1 til 3 mellem planlægning og udførelse, ville den danske stil selv for den dygtigste skribent blive dårlig. Hvor skulle en detalje med, i hvilken rækkefælge skulle tingene skrives, var alt med eller skulle der under indskrivningen det var før PCtidsalderen pludselig tilføjes noget, som medførte et rodet indhold og forskellige abstraktionsniveauer, hvor det burde være samme abstraktionsniveau, osv. Til orientering medførte det ikke automatisk høje karakterer, men hvordan ville det ikke være blevet uden denne planlægning. Men det medførte, at det var muligt med ro i sindet at forlade eksamenslokalet før de sidste hektiske minutter i forvisning om, at det var så det bedste, der kunne produceres på de 4 timer. Mange systemer udvikles uden en tilsvarende grundig bearbejdelse af datamodellen og uden overvejelser om, hvad en dårlig datamodel får af konsekvenser for skrivning af SQL-sætninger og for de applikationer, der skal udarbejdes. Det er vigtigt, at ALLE SQL-sætninger skrives ud fra, hvordan datamodellen er implementeret i databasesystemet og ikke ud fra de regler, udvikleren mener er gældende, eller som udvikleren tester for i sine applikationer. Skal alle tabeller have en surrogatnøgle som primærnøgle en surrogatnøgle er konstant hen over tid, da der ikke er indholdsbetydning i værdien? JA, absolut!! Men kan man da ikke bruge et Cprnummer som primærnøgle? Nej, omkring 50 personer får ændret deres Cprnummer hvert år. Et Cprnummer indeholde både oplysninger om fødselsdato og køn begge informationer kan ændres. Er et gadenavn ikke entydigt inden for en kommune? Jo, indtil sidste kommunesammenlægning. Ved den forrige kommunesammelægning var det et krav, at gadenavnene var entydige men en regel holder kun indtil nogen finder på at ændre den. Og på den måde kan jeg blive ved. Hvis denne simple regel om altid at bruge surrogatnøgler ikke overholdes, opstår der med garanti problemer på et tidspunkt. Er det ikke godt nok hvad det så end er at alle datavalideringer foretages af applikationen. Nej, der er set i hundredevis af eksempler på, at data er blevet indsat i databasen uden om ap- Copyright 2008 Teknologisk Institut side 1 of 10
plikationerne og med fejl til følge. F.eks. loades data fra en komma-separeret fil, så i nogle forekomster er en kolonne NULL, i andre forekomster er værdien den tomme streng og i andre er det en enkelt blank. Gør det søgningerne på en kolonne simplere? Problemet kan være, at det ikke er planlagt fra starten, at data skal indsættes uden om applikationerne, men ved opkøb, sammenlægning ol. skal et andet firmas data medtages i databasen. Jeg hører ofte, at mange har den holdning, at kravet til deres datamodel skal være, at den er på 3NF (3. normalform). Det er også meget bekvemt, da en erfaren modellør ofte laver datamodellen på denne normalform. Derfor er det jo ikke nødvendigt at undersøge om kvaliteten 3. normalform er overholdt. Ved en struktureret gennemgang af datamodellen inden udarbejdelse af diverse applikationer, kan man forsøge at sikre, at alle dataregler er fundet og specificeret og derefter defineret i databasen, så applikationerne kan nøjes med at teste for forretningslogik. Og så er der jo Boyce-Codd normalformen, som kan synliggøre designfejl og 4. normalform, som tester for sammenhænge, som jævnligt ses uden for teoribøgerne. Der findes også andre begreber, som definerer problemstillinger, der skal overvejes. Det kan være, at der findes super/sub-type hierarkier, der bør defineres for at sikre konsistens. Mange designovervejelser handler om at undgå redundans. Hvad får det egentlig af konse kvenser, hvis der er redundans i en database. Hvis der ikke tages højde for det dårlige design, bliver konsekvensen en meget dårligere datakvalitet. Hvis der skal tages højde for redundante data i applikationen, bliver det en mere kompleks applikation. Efterfølgende vil jeg vise nogle eksempler på de problemstillinger, der forekommer, når ens database indeholder redundante data. Senere i artiklen vil jeg se på, hvad det betyder for udarbejdelse af SQL-sætninger. Det er væsentligt at understrege, at man ikke ud fra et givet datamateriale kan se, om der forekommer redundante data, men kun ved at vide, hvilke regler der forekommer i et givet domæne. Er Enhedspris redundant i efterfølgende tabel? OrderLinie OrderId VareId AntalStk Enhedspris 1 12 3 10 1 14 2 14 2 12 1 10 2 13 3 14 2 14 5 14 3 17 1 20 Ja, hvis en vare ALTID skal sælges til den samme Enhedspris eller til den pris, der er gældende på leveringstidspunktet. Men gælder den regel, at sælges der mindst 100 stk., må der gives rabat, forekommer der ikke redundans. Indtil nu er der blot ingen, der har købt mindst 100 stk., derfor er alle værdier i kolonnen Enhedspris for en given vare den samme. Lige nøjagtig denne problemstilling gør det vanskeligt at skrive sine manipulationssætninger, da alle Copyright 2008 Teknologisk Institut side 2 of 10
regler ikke altid kan læses ud fra definitionerne i databasen eller konkluderes ud fra indholdet af tabellen på det tidspunkt, hvor sætningen skrives. Reglen om de 100 stk. kan evt. være beskrevet i en trigger eller i det modul i forretningslaget, som tester for forretningsregler. Det kan også være, at reglen kun er kendt, men ikke beskrevet. Det vil være lettere at skrive sine manipulations-sætninger, hvis man ved, at der ikke forekommer redundans i databasen. Så når kolonnen Enhedspris er med i tabellen, er det fordi den samme vare kan have forskellige priser. Begrundelsen for at have redundante data i en database er stort set altid, at det giver en bedre performance. Dette er også korrekt i nogle tilfælde, men erfaringen viser, at der sjældent testes for, om det også giver en performance-forbedring af en størrelse, som er værd at gå efter. Ofte overses de problemer det giver, at have redundant information i databasen i form af mere komplicerede manipulations-sætninger og/eller applikationer. Der findes grundlæggende 3 former for redundans forekomst-redundans, hvor informationen kan findes i en anden forekomst i samme tabel kolonne-redundans, hvor informationen kan findes i samme forekomst, men i en anden kolonne redundans mellem tabeller, hvor informationen kan findes i en anden tabel direkte eller indirekte Forekomst-redundans er nok den mest udbredte form, men også der, hvor redundans giver de fleste problemer. I efterfølgende eksempel består tabellen Kunde af 3 kolonner Kundenr, Postnr og Bynavn. Kolonnen Kundenr er primærnøgle og kolonnen Bynavn indeholder redundant information, da Bynavn altid skal være det samme for et givet Postnr. Dvs. at tabellen kun er på 2NF, da der forekommer en transitiv afhængighed. Kunde Kundenr Postnr Bynavn 1 2000 Frederiksberg 2 9000 Aalborg 3 8000 Århus 4 9000 Ålborg 5 1127 København K 6 8000 Århus C Hvis der ved indsættelse af en ny forekomst ikke testes for, om Bynavn er stavet på samme måde, som de forekomster der allerede findes i tabellen, vil der ved manipulation med SE- LECT, UPDATE og DELETE skulle testes for alle stavemåder af Bynavn, hvis der i betingelsen refereres til kolonnen Bynavn. Simplere NEJ, mere kompleks kode Ja. For hvilke stavemåder kan der være anvendt. Resultatet vil blive forkert, hvis der testes for Aalborg stavet med Aa og ikke også for den forkerte stavemåde med Å. Hvis der er 10.000 forekomster stavet korrekt med Aa og 100 forekomster stavet med Å er problemet, at fejlen næppe opdages. Copyright 2008 Teknologisk Institut side 3 of 10
Hvis der skal testes for samme stavemåde, vil det gøre indsættelser og opdateringer mere komplicerede. Hvad skal der forøvrigt ske, hvis den forekomst, der skal indsættes, har den korrekte stavemåde og de eksisterende forekomster i tabellen er stavet forkert. Ja, så skal der rettes i de gamle forekomster, før den nye forekomst kan indsættes. Hvad skal der tilføjes af kode i applikationen for at sikre, at gamle forekomster rettes inden den nye forekomst indsættes? Gør det applikationen simplere? Løsningen er desværre ofte, at man lader stå til. Lidt inkonsistens er vel ikke et problem, før der søges på kolonnen og DET OPDAGES, at ikke alle de ønskede forekomster kommer med i resultatet. Kolonne-redundans giver ikke de samme problemer. I tabellen vises et eksempel, hvor der er kolonne-redundans på kolonnerne Aar, Maaned og Dag. Tid Dato Aar Maaned Dag 2008-7-11 2008 7 11 2008-7-12 2008 7 12 2008-7-13 2008 7 13 2008-7-14 2008 7 14 Konstruktionen laves ofte med det formål at kunne indeksere de kolonner, der indeholder redundans for dermed at opnå en bedre performance. Indsættelser og opdateringer bliver en smule mere kompliceret, især hvis den/de redundante kolonner, er beregnet ud fra komplekse formler. Man skal sikre sig, at den samme formel anvendes alle de steder, hvor der indsættes eller opdateres data. Opstår der fejl, kan værdierne i de redundante kolonner altid genskabes ved en simpel manipulation af tabellen. Problemet vil dog aldrig opstå, hvis de redundante kolonner er defineret som en beregnet kolonne, hvad der f.eks. er muligt i SQL Server, eller de redundante værdier beregnes i en trigger. Såfremt der er redundans i en database bør den vedligeholdes i databasesystemet og ikke i applikationerne. Redundans mellem tabeller giver også problemer. Når vi ser på tabellerne Ordre og Ordrelinie, kan vi se, at der i Ordre-tabellen er medtaget en kolonne, der indeholder den totale pris for en ordre. Hver gang der foretages ændringer i Ordrelinie-tabellen, skal Ordre-tabellen også opdateres. I netop dette tilfælde er problemet ikke så stort, da fejlagtige totaler i Ordretabellen altid kan gendannes ved at genberegne ud fra Ordrelinie-tabellen. Ordre OrdreID KundeID Leveringsdato PrisIalt 1 11 11-12-2008 35 2 13 11-12-2008 10 3 11 12-12-2008 220 4 17 16-12-2008 34 Copyright 2008 Teknologisk Institut side 4 of 10
Ordrelinie OrdreID VareID Antal Enhedspris 1 124 2 10 1 167 3 5 2 167 2 5 3 87 1 100 3 432 2 50 3 564 1 20 4 267 2 17 Tager vi problemet som skitseret under forekomst-redundans og udvider det til også at være redundans mellem tabeller, bliver kompleksiteten større. Vi kunne i et system have 3 tabeller med informationer om personer, f.eks. Kunde, Leverandør og Medarbejder. Hvis alle 3 tabeller indeholder såvel Postnr som Bynavn, vil det medføre endnu mere kompleks kode for at teste, om en værdi i kolonnen Bynavn er stavet korrekt. For ved indsættelse af en leverandør med et endnu ikke eksisterende Postnr i Leverandør-tabellen, skal stavemåden af værdien i kolonnen Bynavn testes op mod tabellen Kunde og/eller tabellen Medarbejder. Hvis værdien i kolonnen Bynavn nu er stavet forkert i en af disse tabeller, har brugeren af systemet måske ikke lov til at rette i disse tabeller. Konsekvensen er, at enten indsættes leverandøren med værdien i kolonnen Bynavn stavet forkert eller også hvad der i praksis viser sig at være den mest gængse løsning der testes ikke. Så hvis du gerne vil sikre dig konsistente data, skal du sørge for at arbejde med normaliserede data. Performance-forbedringer specielt i TP-systemer ved at arbejde på redundante data, giver måske kun forbedringer, fordi der skabes inkonsistente data, og dermed et system med en dårligere datakvalitet. Derfor skal du ved test sikre dig, at det også giver den ønskede performance-forbedring. Men udarbejdelse af manipulations-sætninger bliver i alle tilfælde mere kompliceret. I det efterfølgende eksempel skal vi udføre en UNION-operationen på tabellen Kunde og tabellen Leverandor. Det er interessant at finde ud af, om de 2 tabeller har en fællesmængde, dvs. om der findes kunder der også er leverandører. Findes der ikke en fællesmængde, skal operatoren UNION ALL anvendes i stedet for UNION. Performance-forbedringer skal opnås ved at bruge den rigtige operator. Hvis vi kommer frem til, at en forekomst fra tabellen Kunde med det samme værdi i kolonnen Tlfnr, som en forekomst fra tabellen Leverandor med samme værdi i kolonne Tlfnr, skal opfattes som værende den samme information, har de 2 tabeller en fællesmængde. Vi skal derfor anvende UNION. Problemet er imidlertid, at der kan være forskel på Navn og/eller Adresse i 2 forekomster med samme værdi i kolonnen Tlfnr. Desuden vil der med meget stor sandsynlighed altid være forskel på primærnøglerne, da dette blot er et fortløbende nummer inden for hver tabel. Anvender vi sætningen Copyright 2008 Teknologisk Institut side 5 of 10
SELECT * UNION SELECT * FROM Leverandor får vi ikke fjernet de dubletter, som vi gerne vil have fjernet. Derfor er vi nødt til at løse problemet på en anden måde. For at vise et par løsnings-eksempler anvendes følgende tabeller. CREATE TABLE Kunde ( KundeID INT NOT NULL PRIMARY KEY, Navn VARCHAR(30) NOT NULL, Adresse VARCHAR(30) NOT NULL, Postnr SMALLINT NOT NULL, KundeType CHAR(1) NOT NULL, Tlfnr VARCHAR(8) NOT NULL UNIQUE) CREATE TABLE Leverandor ( LeverandorID INT NOT NULL PRIMARY KEY, Navn VARCHAR(30) NOT NULL, Adresse VARCHAR(30) NOT NULL, Postnr SMALLINT NOT NULL, Tlfnr VARCHAR(8) NOT NULL UNIQUE) GO INSERT INTO Kunde VALUES (1, 'Jens Hansen', 'Nygade 2', 8000, 'A, 12345678') INSERT INTO Kunde VALUES (2, 'Peter Olesen', 'Vestergade 23', 9000, 'A, '98765432') INSERT INTO Kunde VALUES (3, 'Ida Hansen', 'Torvet 8', 8000, 'C, '11223344') INSERT INTO Kunde VALUES (4, 'Sanne Carlsen', 'Søndergade 33', 2000, 'B, '99887766') INSERT INTO Kunde VALUES (5, 'Finn Poulsen', 'Poulsgade 62', 7000, 'B, '11114444') INSERT INTO Leverandor VALUES (1, 'Ida Hansen', 'Torvet 8', 8000, '11223344') INSERT INTO Leverandor VALUES (2, 'Sanne Carlsen', 'Søndergade 33 2. tv.', 2000, '99887766') INSERT INTO Leverandor VALUES (3, 'Finn Poulsen', 'Poulsgade 62', 7000, '11114444') INSERT INTO Leverandor VALUES (4, 'Slagteren', 'Østergade 3', 4000, '66662222') INSERT INTO Leverandor VALUES (5, 'Bageren', 'Nygade 17', 5000, '88884444') Når vi udfører sætningen, får vi forekomster med i resultatet, som opfattes som værende dubletter, selvom KundeId hhv. LeverandorId er udeladt, da der vil være forskelle på indeholdet i andre kolonner i de 2 tabeller. SELECT Navn, Adresse, Postnr, Tlfnr UNION SELECT Navn, Adresse, Postnr, Tlfnr FROM Leverandor ORDER BY Tlfnr Udføres sætningen, får vi det efterfølgende resultat. Kunde med KundeId = 5 og Leverandor med LeverandorId = 3 er kun med i resultatet én gang, da Navn, Adresse, Postnr og Tlfnr er angivet nøjagtig ens i de 2 tabeller. Det samme gælder for Kunde og Leverandor med Navn = Copyright 2008 Teknologisk Institut side 6 of 10
Ida Hansen. Men Navn = Sanne Carlsen er med 2 gange i resultatet, da der er forskel på værdien i kolonnen Adresse i de 2 tabeller. Navn Adresse Postnr Tlfnr Finn Poulsen Poulsgade 62 7000 11114444 Ida Hansen Torvet 8 8000 11223344 Jens Hansen Nygade 2 8000 12345678 Slagteren Østergade 3 4000 66662222 Bageren Nygade 17 5000 88884444 Peter Olesen Vestergade 23 9000 98765432 Sanne Carlsen Søndergade 33 2000 99887766 Sanne Carlsen Søndergade 33 2. tv. 2000 99887766 Hvordan løses dette problem? Da det var kolonnen Tlfnr, som skulles bruges til at finde dubletter, kan vi udføre UNIONoperatoren på denne kolonne. Dette resultat kaldes i sætningen som er vist efterfølgende for KL_Tlf. Vores endelig resultat-tabel skal indeholde lige så mange forekomster som i denne tabel. Vi skal derefter join e tabellerne KL_Tlf, Kunde og Leverandor. Nogle gange skal kolonnerne Navn, Adresse og Postnr tages fra tabellen Kunde andre gange fra tabellen Leverandor. Derfor anvender vi en LEFT JOIN mellem KL_Tlf og tabellerne Kunde og Leverandor. Da vi enten skal tage værdierne fra Kunde eller fra Leverandor anvendes ISNULL. Kunde-kolonnerne vil være NULL, hvis det er en Leverandor og resultatet vil indeholde værdierne fra Leverandor-kolonnerne. På samme måde vil Leverandor-kolonnerne være NULL, hvis det er en Kunde og Kunde-kolonnernes værdi medtages i resultatet. For forekomster i fællesmængden er der både værdier for kolonnerne i tabellen Kunde og for kolonnerne i tabellen Leverandor. I den viste løsning vælges værdierne fra tabellen Kunde Kunde-kolonnerne er første parameter til funktionen ISNULL. SELECT KL_Tlfnr.Tlfnr, ISNULL(Kunde.Navn, Leverandor.Navn) AS Navn, ISNULL(Kunde.Adresse, Leverandor.Adresse) AS Adresse, ISNULL(Kunde.Postnr, Leverandor.Postnr) AS Postnr FROM (SELECT Tlfnr UNION SELECT Tlfnr FROM Leverandor) AS KL_Tlfnr LEFT JOIN Kunde ON KL_Tlfnr.Tlfnr = Kunde.Tlfnr LEFT JOIN Leverandor ON KL_Tlfnr.Tlfnr = Leverandor.Tlfnr Som vi kan se af resultatet, er Navn = Sanne Carlsen kun med 1 gang i resultat-tabellen. En vigtig forudsætning for denne løsning er også, at kolonnen Tlfnr er defineret som værende entydig i hver af de 2 tabeller. UNION i sub-select en fjerner ikke kun dublerede Tlfnr mellem de 2 tabeller, men også evt. dublerede værdier i kolonnen Tlfnr inden for den samme tabel. Copyright 2008 Teknologisk Institut side 7 of 10
Problemet er, at den efterfølgende LEFT JOIN vil danne flere forekomster med det samme Tlfnr i resultat-tabellen. Dette problem kan løses med en APPLY JOIN, men dette vil ikke blive gennemgået her. Navn Adresse Postnr Tlfnr Finn Poulsen Poulsgade 62 7000 11114444 Ida Hansen Torvet 8 8000 11223344 Jens Hansen Nygade 2 8000 12345678 Slagteren Østergade 3 4000 66662222 Bageren Nygade 17 5000 88884444 Peter Olesen Vestergade 23 9000 98765432 Sanne Carlsen Søndergade 33 2000 99887766 Men er denne måde at løse problemet på altid brugbar. Her er det vigtigt at se på definitionen af de 2 tabeller. Da alle kolonner i tabellen Kunde er defineret med NOT NULL, vil kolonnerne fra tabellen Kunde efter LEFT JOIN kun være NULL for de forekomster, hvor informationerne skal tages fra tabellen Leverandor. Så løsningen giver med denne definition af tabellerne, det korrekte resultat. Hvis en af kolonnerne i tabellen Kunde accepterer NULL, vil der måske være et problem. For alle forekomster i fællesmængden af de 2 tabeller, vil data komme fra tabellen Kunde. Men er der NULL i en kolonne fra Kunde, vil data komme fra tabellen Leverandor. Vi får derfor blandet data fra de 2 tabeller i en forekomst i resultat-tabellen. Eksemplet viser, at i tabellen Kunde må Postnr være NULL. Kunde Navn Adresse Postnr Tlfnr Firma A/S Poulsgade 62 NULL 11114444 Leverandor Navn Adresse Postnr Tlfnr Firma A/S Vestergade 11 3000 11114444 I resultatet vil værdien i kolonnen Adresse komme fra tabellen Kunde, men Postnr tages fra tabellen Leverandor. Det er jo ikke sikkert, at kombinationen Adresse = Poulsgade 62 og Postnr = 3000 forekommer. Selvfølgelig er det et problem, at der er angivet en værdi i kolonnen Adresse, og at der mangler en værdi i kolonne Postnr, men det er netop en af de fejl der opstår, når tabellen ikke er defineret præcist og begrænsende nok. Og den uheldige kombination forekommer måske først efter lang tids brug af systemet. Resultat-tabel Navn Adresse Postnr Tlfnr Firma A/S Poulsgade 62 3000 11114444 Vi skal derfor lave en anden løsning af problemet, hvor vi sikrer at alle data i en forekomst i resultat-tabellen enten kommer fra tabellen Kunde eller kommer fra tabellen Leverandor. Copyright 2008 Teknologisk Institut side 8 of 10
Problemet kunne løses ved først at finde Alle forekomster i Kunde, som ikke findes i Leverandor ved sammenligning på Tlfnr difference-mængden mellem Kunde og Leverandor. Alle forekomster i Leverandor, som ikke findes i Kunde ved sammenligning på Tlfnr - difference-mængden mellem Leverandor og Kunde. Alle forekomster i Leverandor, som også findes i Kunde ved sammenligning på Tlfnr fællesmængden mellem Leverandor og Kunde. På disse 3 mellemresultater udføres en UNION ALL. Det er meget vigtigt at konstatere, at differencemængden og fællesmængden ikke er fundet ved at sammenligne forekomst med forekomst ved at bruge operatorerne EXCEPT og IN- TERSECT - men kun ved at sammenligne på kolonnen Tlfnr, som er den kolonne der skal afgøre, om forekomsterne skal opfattes som værende samme information. Sætningen kommer til at se således ud. SELECT Navn, Adresse, Postnr, Tlfnr WHERE NOT EXISTS ( SELECT * FROM Leverandor WHERE Kunde.Tlfnr = Leverandor.Tlfnr) UNION ALL SELECT Navn, Adresse, Postnr, Tlfnr FROM Leverandor WHERE NOT EXISTS ( SELECT * WHERE Kunde.Tlfnr = Leverandor.Tlfnr) UNION ALL SELECT Navn, Adresse, Postnr, Tlfnr FROM Leverandor WHERE EXISTS ( SELECT * WHERE Kunde.Tlfnr = Leverandor.Tlfnr) Det er også vigtigt at understrege, at valg af løsningsforslag kun drejer sig om det korrekte resultat og ikke om performance eller læsbarhed af sætning. Det korrekte resultat vil altid komme forud for performance. Den føste simple sætning med udførelse af UNION-operatoren på de 2 tabeller, ser ud til at løse problemet. Begge tabeller er på 3NF og umiddelbart er designet, som det skal være. De 2 tabeller har defineret en PRIMARY KEY, som er en surrogatnøgle. Desuden er kolonnen Tlfnr i begge tabeller angivet som værende UNIQUE. Alle kolonner skal have en værdi NOT NULL. Så der er gjort mange overvejelser ved definition af tabellerne. Copyright 2008 Teknologisk Institut side 9 of 10
I det viste eksempel er problemet dog, at der kun bør være én tabel, der indeholder kolonnerne Navn, Adresse og Postnr. Hvis der under design-processen var defineret et super/sub-type hierarki med en super-tabel NavnAdresse og 2 sub-type tabeller Kunde og Leverandoer, ville løsningen på ovenstående problem være en simpel SELECT-sætning på super-tabellen NavnAdresse. Men med 2 tabeller skal en af de 2 sidste sætninger anvendes, for at få det korrekte resultat. Som ovenstående eksempler viser, er det meget vigtigt, at den datamodel, der danner grundlag for implementering af databasen, er gennemanalyseret. Ved definition af databasen er det vigtigt, at alle de begrænsninger, der er fundet ved analyse af domænet, medtages. Der må kun tages højde for de begrænsninger, der er defineret i databasen, når der skal udarbejdes manipulations-sætninger. Brug derfor den nødvendige tid på udarbejdelse af datamodellen. Sørg for, at der er testdata der afspejler definitionen af de enkelte kolonner/tabeller, og ikke kun testdata som verdenen ser ud nu. Lav sætninger, der også virker, når de usandsynlige data alligevel bliver gemt i tabellerne. Lad være med at nare dig selv ved letkøbte argumenter, som f.eks. at 3NF er godt nok. Så du bør sikre dig, at dine data altid har en høj kvalitet. Det gør det også lettere, når du på et senere tidspunkt evt. skal anvende dine data i f.eks. en Business Intelligence løsning. Og så er det jo sjovere at videreudvikle i stedet for at bruge tiden på tåbelige og uforståelige fejl, der pludselig opstår. Copyright 2008 Teknologisk Institut side 10 of 10