Gå til hovedinnhold

Beste praksis

Her beskriver vi beste praksis for database-utvikling. Dette er føringer som de fleste bør følge.

Språkstøtte i databaser

Det finnes flere måter å implementere språkstøtte i databaser. Måten en velger å implementere språkstøtte på må vurderes ut i fra behovene i hvert enkelt brukstilfelle.

Som hovedregel foretrekker vi en normalisert løsning hvor de oversatte tekstene ligger i sin egen tabell.

Behov for forretningsregler som at det alltid skal finnes tekster på et eller flere spesifikke språk vil løses best ved å legge inn kolonner for disse i hovedtabellen med tilhørende check constraints. I så fall bør man se til Egne kolonner per språkvariant. Denne denormaliserte varianten er også å verdt å se nærmere på dersom man har behov for å filtrere og sortere på tekstverdiene i tillegg til andre attributter og man opplever ytelsesproblemer.

Anbefalt normalisert løsning

I denne løsningen vil oversettelser ligge i en egen tabell som er koblet til hovedtabellen ved hjelp av fremmednøkler. Oversettelsene bør ligge i det samme skjemaet som hovedtabellen, mens språktabellen bør deles på tvers.

create table sprak (
sprakkode text not null constraint sprak_pk primary key,
spraknavn text not null
);

create table produkt (
produkt_id bigint constraint produkt_pk primary key generated always as identity,
sprakkode_original text constraint produkt_originalsprak references sprak(sprakkode),
navn_original text not null,
beskrivelse_original text not null
);

create table produkt_oversettelser (
produkt_id bigint not null constraint produktoversettelser_produkt_fk references produkt(produkt_id),
sprakkode text not null constraint produktoversettelser_sprak_fk references sprak(sprakkode),
navn text not null,
beskrivelse text not null
);

Egne kolonner per språkvariant

Her legges språkvariantene i egne kolonner i hovedtabellen. Kolonner bør da bruker språknavnet som postfiks. Dette er et pragmatisk godt valg dersom man har et begrenset sett med språk man trenger å støtte. I tillegg gjør denne løsningen det lett å legge inn forretningsregler knyttet til språk.

I det følgende eksempelet viser vi hvordan vi kan ha en språkkode i hovedtabellen for å spesifisere originalspråk, samt hvordan vi kan bruke check constraints for å sørge for at enkelte oversettelser er påkrevet.

create table produkt (
produkt_id bigint constraint produkt_pk primary key generated always as identity,

-- Oversettelser
-- Ekstra regel: må alltid ha bokmål
navn_bokmal text not null,
beskrivelse_bokmal text not null,
navn_engelsk text check (sprakkode_original != 'engelsk' or navn_engelsk is not null),
beskrivelse_engelsk text check (sprakkode_original != 'engelsk' or beskrivelse_engelsk is not null),
navn_nynorsk text check (sprakkode_original != 'nynorsk' or navn_nynorsk is not null),
beskrivelse_nynorsk text check (sprakkode_original != 'nynorsk' or beskrivelse_nynorsk is not null),
navn_samisk text check (sprakkode_original != 'samisk' or navn_samisk is not null),
beskrivelse_samisk text check (sprakkode_original != 'samisk' or beskrivelse_samisk is not null),

-- Spesifiser originalspråk, bruk forretningsregler for originaltekster
sprakkode_original text not null constraint produkt_sprak check (sprakkode_original in ('bokmal', 'engelsk', 'nynorsk', 'samisk')),
navn_original text not null
generated always as (
case sprakkode_original
when 'bokmal' then navn_bokmal
when 'engelsk' then navn_engelsk
when 'nynorsk' then navn_nynorsk
when 'samisk' then navn_samisk
end
) stored,
beskrivelse_original text not null
generated always as (
case sprakkode_original
when 'bokmal' then beskrivelse_bokmal
when 'engelsk' then beskrivelse_engelsk
when 'nynorsk' then beskrivelse_nynorsk
when 'samisk' then beskrivelse_samisk
end
) stored
);

Én kolonne med json-verdi

Man kan pakke inn språkvariantene i en json-verdi. Dette gir veldig stor fleksibilitet, så man bør sørge for å sikre at json-verdien er i henhold til et fornuftig skjema. I PostgreSQL finnes for eksempel utvidelsen pg_jsonschema som muligens vil gjøre dette lettere, se for eksempel denne bloggposten for mer info). Alternativt kan man skrive check constraints selv.

Dette er IKKE et mønster vi anbefaler, med mindre man har behov for fleksibiliteten dette gir.

Frittstående koblingstabell med generell ID

Her skiller vi ut språkvarianter i en egen koblingstabell (i Kodeverk) som ikke har en relasjon (fremmednøkkel) til tabellen den representerer. I stedet benytter koblingstabellen tabellnavn/kolonnenavn i nøkkelen for å henvise til riktig tabell og kolonne, samt en generell ID (som må kunne kobles mot alle aktuelle nøkkelstrukturer).

Dette er IKKE et mønster vi anbefaler.

Frittstående koblingstabell med alle nøkkelvarianter

Som alternativet over, opererer denne med en generell koblingstabell (i Kodeverk). Forskjellen er at i stedet for en generell ID (som må kunne kobles mot alle aktuelle nøkkelstrukturer) er alle mulige nøkkelattributter tilstede i koblingstabellen. Dermed er det mulig å definere fremmednøkler mot alle aktuelle tabeller (basert på hvilken kombinasjon av attributter som er satt). Da disse nøkkelattributtene må være valgfrie, må kombinasjonen av dem være en sekundærnøkkel. Dvs. at tabell-komponenten i PK utgår, mens ID-komponenten fortsatt er en generell ID, men den har ingenting med nøkkelen i den aktuelle tabellen å gjøre.

Dette er IKKE et mønster vi anbefaler.