Selv om du undrer dig over, hvad Postgresql skemaer er, hvorfor de er vigtige, og hvordan du kan bruge skemaer til at gøre dine databaseimplementationer mere robuste og vedligeholdelsesvenlige? Denne artikel vil introducere det grundlæggende om skemaer i Postgresql og vise dig, hvordan du opretter dem med nogle grundlæggende eksempler. Fremtidige artikler vil dykke ned i eksempler på, hvordan du kan sikre og bruge skemaer til virkelige applikationer.
Først, for at rydde op i potentiel terminologisk forvirring, skal vi forstå, at i Postgresql-verdenen er udtrykket “skema” måske lidt uheldigt overbelastet. I den bredere kontekst af relationelle databasestyringssystemer (RDBMS) kan udtrykket “skema” forstås som en henvisning til databasens overordnede logiske eller fysiske design, dvs. definitionen af alle tabeller, kolonner, visninger og andre objekter, der udgør databasedefinitionen. I denne bredere sammenhæng kan et skema udtrykkes i et ER-diagram (entity-relationship-diagram) eller et script af DDL-angivelser (data definition language), der bruges til at instantiere applikationsdatabasen.
I Postgresql-verdenen kan begrebet “skema” måske bedre forstås som et “namespace”. I Postgresql-systemtabellerne registreres skemaer faktisk i tabelspalter kaldet “name space”, hvilket IMHO er en mere korrekt terminologi. Som en praktisk sag, når jeg ser “schema” i forbindelse med Postgresql, omfortolker jeg det stille og roligt som “name space”.
Men du spørger måske: “Hvad er et name space?” Generelt er et navnerum et ret fleksibelt middel til at organisere og identificere information ved hjælp af navne. Forestil dig f.eks. to nabohusstande, familien Smith, Alice og Bob, og familien Jones, Bob og Cathy (jf. figur 1). Hvis vi kun brugte fornavne, kunne det blive forvirrende at finde ud af, hvilken person vi mente, når vi talte om Bob. Men ved at tilføje efternavnet, Smith eller Jones, identificerer vi entydigt, hvilken person vi mener.
Ofte er navnerum organiseret i et indlejret hierarki. Dette giver mulighed for en effektiv klassificering af store mængder information i en meget finkornet struktur, som f.eks. internetdomænenavnsystemet. På det øverste niveau definerer “.com”, “.net”, “.org”, “.edu” osv. brede navnerum, inden for hvilke der er registreret navne for specifikke enheder, så for eksempel “severalnines.com” og “postgresql.org” er entydigt defineret. Men under hvert af disse er der en række fælles subdomæner som f.eks. “www”, “mail” og “ftp”, som i sig selv er duplikerende, men som inden for de respektive navnerum er unikke.
Postgresql-skemaer tjener samme formål med at organisere og identificere, men i modsætning til det andet eksempel ovenfor kan Postgresql-skemaer ikke være indlejret i et hierarki. Selv om en database kan indeholde mange skemaer, er der altid kun ét niveau, og derfor skal skemanavnene inden for en database være unikke. Desuden skal hver database indeholde mindst ét skema. Når en ny database oprettes, oprettes der et standardskema med navnet “public”. Indholdet af et skema omfatter alle andre databaseobjekter som f.eks. tabeller, views, lagrede procedurer, triggers osv. For at visualisere dette henvises til figur 2, som viser en matryoshka-dukke-lignende indlejring, der viser, hvor skemaer passer ind i strukturen af en Postgresql-database.
Udover blot at organisere databaseobjekter i logiske grupper for at gøre dem mere overskuelige, tjener skemaer det praktiske formål at undgå navnekollisioner. Et operationelt paradigme indebærer, at der defineres et skema for hver databasebruger for at skabe en vis grad af isolation, et rum, hvor brugerne kan definere deres egne tabeller og visninger uden at forstyrre hinanden. En anden fremgangsmåde er at installere tredjepartsværktøjer eller databaseudvidelser i individuelle skemaer for at holde alle de relaterede komponenter logisk sammen. I en senere artikel i denne serie vil der blive redegjort for en ny tilgang til robust applikationsdesign, hvor man anvender skemaer som et indirekte middel til at begrænse eksponeringen af databasens fysiske design og i stedet præsentere en brugergrænseflade, der løser syntetiske nøgler og letter vedligeholdelse og konfigurationsstyring på lang sigt, efterhånden som systemkravene udvikler sig.
Lad os lave noget kode!
Den enkleste kommando til at oprette et skema i en database er
CREATE SCHEMA hollywood;
Denne kommando kræver create-privilegier i databasen, og det nyoprettede skema “hollywood” vil være ejet af brugeren, der påkalder kommandoen. En mere kompleks invocation kan omfatte valgfrie elementer, der angiver en anden ejer, og kan endda omfatte DDL-anvisninger, der instantierer databaseobjekter inden for skemaet, alt sammen i én kommando!
Det generelle format er
CREATE SCHEMA schemaname ]
hvor “brugernavn” er, hvem der skal eje skemaet, og “schema_element” kan være en af visse DDL-kommandoer (se Postgresql-dokumentationen for nærmere oplysninger). Der kræves superbrugerrettigheder for at bruge indstillingen AUTHORIZATION.
Så for eksempel for at oprette et skema ved navn “hollywood”, der indeholder en tabel ved navn “films” og en visning ved navn “winners” i én kommando, kan du gøre
CREATE SCHEMA hollywood CREATE TABLE films (title text, release date, awards text) CREATE VIEW winners AS SELECT title, release FROM films WHERE awards IS NOT NULL;
Der kan efterfølgende oprettes yderligere databaseobjekter direkte, for eksempel vil en ekstra tabel blive tilføjet til skemaet med
CREATE TABLE hollywood.actors (name text, dob date, gender text);
Bemærk i ovenstående eksempel, at tabelnavnet er præfikseret med navnet på skemaet. Dette er nødvendigt, fordi nye databaseobjekter som standard, dvs. uden eksplicit skemaspecifikation, oprettes inden for det aktuelle skema, som vi vil behandle næste gang.
Husk, hvordan vi i det første navnerumseksempel ovenfor havde to personer, der hed Bob, og vi beskrev, hvordan vi kunne afkonflikte eller skelne dem ved at inkludere efternavnet. Men inden for hver af husstandene Smith og Jones for sig forstår hver familie, at “Bob” henviser til den, der hører til den pågældende husstand. Så f.eks. i forbindelse med hver af de respektive husstande behøver Alice ikke at tiltale sin mand som Bob Jones, og Cathy behøver ikke at tiltale sin mand som Bob Smith: de kan hver især bare sige “Bob”.
Det aktuelle Postgresql-skema er lidt ligesom husstanden i det ovenstående eksempel. Objekter i det aktuelle skema kan refereres ukvalificeret, men for at referere til objekter med samme navn i andre skemaer skal navnet kvalificeres ved at sætte skemanavnet i præfiks som ovenfor.
Det aktuelle skema er afledt af konfigurationsparameteren “search_path”. Denne parameter gemmer en kommasepareret liste over skemanavne og kan undersøges med kommandoen
SHOW search_path;
eller sættes til en ny værdi med
SET search_path TO schema ;
Det første skemanavn på listen er det “aktuelle skema” og er der, hvor nye objekter oprettes, hvis det er angivet uden kvalificering af skemanavnet.
Den kommaseparerede liste over skemanavne tjener også til at bestemme den søgeorden, hvormed systemet finder eksisterende ukvalificerede navngivne objekter. For eksempel, tilbage til Smith og Jones-kvarteret, vil en pakkeløsning, der kun er adresseret til “Bob”, kræve besøg i hver husstand, indtil den første beboer, der hedder “Bob”, er fundet. Bemærk, at dette måske ikke er den tiltænkte modtager. Den samme logik gælder for Postgresql. Systemet søger efter tabeller, views og andre objekter i skemaer i den rækkefølge, der er angivet i search_path, og derefter anvendes det første fundne navnematchobjekt. Skema-kvalificerede navngivne objekter bruges direkte uden henvisning til search_path.
I standardkonfigurationen viser forespørgsel på konfigurationsvariablen search_path denne værdi
SHOW search_path; Search_path-------------- "$user", public
Systemet fortolker den første værdi, der er vist ovenfor, som det aktuelle loggede brugernavn og imødekommer den tidligere nævnte brugssituation, hvor hver bruger er tildelt et brugernavngivet skema til et arbejdsområde, der er adskilt fra andre brugere. Hvis der ikke er oprettet et sådant skema med brugernavn, ignoreres denne post, og det “offentlige” skema bliver det aktuelle skema, hvor nye objekter oprettes.
Sådan, tilbage til vores tidligere eksempel med oprettelse af tabellen “hollywood.actors”, hvis vi ikke havde kvalificeret tabellens navn med skemanavnet, ville tabellen være blevet oprettet i det offentlige skema. Hvis vi forventede at oprette alle objekter inden for et bestemt skema, kunne det være praktisk at indstille search_path-variablen som
SET search_path TO hollywood,public;
, hvilket gør det nemmere at skrive ukvalificerede navne for at oprette eller få adgang til databaseobjekter.
Der er også en systeminformationsfunktion, som returnerer det aktuelle skema med en forespørgsel
select current_schema();
I tilfælde af fedtfingre i stavemåden kan ejeren af et skema ændre navnet, forudsat at brugeren også har create-privilegier for databasen, med
ALTER SCHEMA old_name RENAME TO new_name;
Og endelig kan man slette et skema fra en database, er der en drop-kommando
DROP SCHEMA schema_name;
DROP-kommandoen fejler, hvis skemaet indeholder objekter, så de skal slettes først, eller du kan eventuelt rekursivt slette et skema alt dets indhold med CASCADE-indstillingen
DROP SCHEMA schema_name CASCADE;
Disse grundlæggende oplysninger vil få dig i gang med at forstå skemaer!