Lærebog til emne 4. Kapitel 20 – 24

Indholdsfortegnelse

Kapitel 20. Introduktion til databaser

Kan du huske, at jeg helt tilbage i kapitel 5 viste dig det store overblik over kodesprog. Den gode, gamle inddeling i frontend/præsentationslag, backend/forretningslag og databaser holder stadigvæk.

3 lags arkitektur

Den næste figur giver efterhånden bedre mening

  • Du kan lave prototyper i f.eks. PowerPoint
  • Du kan billedbehandle i GIMP, Photoshop eller lignende
  • Du kan frontend kodning dvs. HTML, CSS og javaScript. Du ved også, hvad de 3 sprog bruges til hver især
  • Du forstår, hvorfor vi placerer kode på servere, skjult for brugerne
  • Du kender et backend sprog, PHP, som er af de meste populære serverside sprog til hjemmesider
  • Vi skal nu i gang med databaser

Den overordnede plan for gennemgangen af databaser er sådan her.

  • Repetition af databaser fra niveau C
  • Udvide dit kendskab til SQL, så du kan hente informationer fra databasen
  • Normalisering: Vejen fra data, skrevet på bagsiden af en serviet og til en fuld professionel og funktionsdygtig database
  • Kodning. Vi bruger PHP i kombination med SQL for at kommunikere fra backend til databasen og tilbage igen
  • Mission complete: Så kan du lave et program, der omfatter både frontend, backend og database

Link til toppen.

20.1 Hvad er en database?

Niels Ny er lige skiftet skole. Han er flink og rar, men han er ikke så god til navne. Allerede den første dag snakker han med 2 søde piger. Han skriver lidt ned om dem på et efterladt stykke papir, så han bedre kan huske dem.

NAVNHÅRFARVEØVRIGT
LeneRødLIlle, går i smart tøj, dyrker fitness
LoneBrunHøj, går i joggingtøj, kan lide at gå på café

Har Niels Ny en database? Stort JA, Niels Ny har en lille men fuldt gyldig database. En database er som udgangspunkt intet andet end en tabel.

Tabellen kan være opbygget på mange indviklede måder, og man kan dele tabellen op, hvis man vil. Men i bund og grund er en database bare en tabel og intet andet. Til gengæld kan databaser spænde fra Niels Ny’s database og til enorme databaser. Tænk f.eks. på bankernes databaser over alle kundernes transaktioner, databaser med alle bogføringer i en virksomhed, data over temperaturmålinger i Danmark de sidste 20 år, CPR registret osv.

Hvorfor databaser når vi kan gemme data i arrays, cookies og variable

Vi bruger en database til at gemme data i. Du ved, at vi kan gemme data i f.eks. arrays, cookies og variable, men de “dør”, når vi slukker programmet. Derfor har vi brug for databaser.

Link til toppen.

20.2 Database begreber

Primærnøgler, redundans og konsistens er “sygt vigtige” begreber, så derfor gennemgår jeg dem her. Begreberne er ikke så svære, som de lyder

Primærnøgle

Se igen på Niels Ny’s database.

NAVNHÅRFARVEØVRIGT
LeneRødLIlle, går i smart tøj, dyrker fitness
LoneBrunHøj, går i joggingtøj, kan lide at gå på café

Den fungerer fint for Niels. Lad os nu sige, at Niels lærer en ny pige at kende. Hun hedder tilfældigvis også Lene, hun er rødhåret og ligner den anden Lene på en prik. Hvordan kender han så forskel?

Svaret er, at han må lave en kolonne med noget unikt, dvs. et felt med ID. Alle felter skal have et ID-felt med en unik værdi. Vi kalder et sådan felt for en primærnøgle. Uden en primærnøgle kan Niels sagtens tage fejl af de 2 piger, som begge hedder Lene. Uden et ID kan man tage fejl af gode og dårlige kunder, man kan give den forkerte medicin til folk og der kan ske alle mulige andre ulykker.

Alt i alt: Biler har nummerplader, byer har postnumre og vi danskere har CPR som id. Men hvis der ikke findes et ID, så må vi lave en. Det er derfor, at alle kunder har et kundenummer, alle studerende har et studienummer osv.

Inkonsistens

Niels Ny møder en ny og sød pige til en fest, hvor han ikke er helt ædru. Da han kommer hjem, kan han ikke huske, om hun hed Leonora eller Lise. Han tager chancen og skriver “Lise”. Niels har nu en inkonsistent database. Det betyder, at han ikke kan stole på hans database, og så er den reelt ubrugelig.

Redundans

Redundans betyder “overflødig”. Hvis vi har data, som er overflødige, er der redundans i vores database. Et eksempel: Vi har en kunde, som bor i Kolding. Vi registrerer postnummer 6000 og by Kolding. Men: “Kolding” er redundant. Hvis vi først har skrevet postnummer 6000, så kan kunden ikke bo i hverken Vejle eller Fredericia. Så feltet “by” afhænger af feltet “postnummer”, og feltet “By” er overflødigt eller redundant.

Redundante data gør vores database større end nødvendigt og endda sværere at vedligeholde. Mere om redundans under afsnittet om normalisering.

Flere begreber

Som du ved, så består databaser af tabeller. Vi kalder bare nogle af tingene for noget andet.

BEGREB ALMINDELIG TABELTABEL I DATABASE
Orientering: Dvs. hvor er overskrifterneVi kan sætte overskrifter over, til venstre, begge steder, vi bestemmer selvOverskrifter står altid øverst, aldrig i siderne. En overskrift hedder et “feltnavn”
KolonneEn kolonne er en lodret søjleEn kolonne er en lodret søjle, men der er altid en overskrift på
RækkeEn række er en vandret søjleEn række kaldes en post
CelleCelleFelt

Det kan illustreres således. Først tabellen. Bemærk, at i en “almindelig” tabel f.eks. i Excel kunne overskrifterne lige så godt stå til venstre og/eller højre som øverst.

Almindelige tabeller begreber

Her er en tilsvarende figur for tabeller i databaser. Bemærk at overskrifterne altid står øverst, og at der aldrig er overskrifter til hverken venstre eller højre.

Link til toppen.

20.3 Normalisering

Niels Ny’s database fra sidste afsnit er udmærket, og den fungerer fint for ham i hvert fald indtil videre. Når han får Lene eller Lone nr. 5 på listen, så kan han let blande dem sammen. Så for at vi kan arbejde professionelt med databaser, er vi nødt til gøre nogle ting ved dem. Denne proces kalder man for “normalisering. I informatik C har vi kort kikket på følgende 3 nødvendige trin. Trinnene skal udføres i den rækkefølge, de står her:

  • 0. normalform. Det er vores basis, dvs. når vi bare noterer de ønskede men ustrukturerede oplysninger ned på et stykke papir
  • 1. normalform.. Der skal være et id, også kaldet en nøgle eller primærnøgle samt atomisering
  • 2. og 3 normalform. Undgå eventuel redundans ved at dele dine data op

Det kan illustreres sådan her:

Normalisering

O. normalform. Rådata

O. normalform er super simpel. Det øjeblik, Niels Ny skriver det første bogstav i hans lille tabel, så har vi en database på 0. normalform. Du kan sammenligne 0. normalform med ubearbejdede rådata.

1. normalform. ID og “atomisering”

Du skal have et id ud for hver række. Nogle gange har vi et naturligt ID. Alle danskere har et CPR nummer, og f.eks. inden for Sundhedsvæsnet eller hos Skattevæsnet og Politiet vil det være et naturligt id. Hvis vi ikke har et naturligt id, så opfinder vi bare et. F.eks. har alle kundedatabaser opfundet kundenumre til kunderne. Nummeret er ligegyldige, bare alle numre er unikke

Ordet “atom” er græsk og betyder “udelelig”. “Atomisering” handler om, at vi splitter data til “atomer”. Hvis vi f.eks. vil sortere kunder efter både fornavn og efternavn, så skal vi selvfølgelig dele “navn” op i 2 forskellige kolonner. Vi behøves ikke altid at dele data op, f.eks. skiller vi sjældent vejnavn og husnummer ad.

2. og 3 normalform. Undgå eventuel redundans ved at dele dine data op

Redundans er overflødige data. Hvis vi skriver det samme oplysning flere gange. Hvis vi nu har flere kunder i Esbjerg, så kommer vi til at skrive kombinationen postnummer – by for Esbjerg flere gange. Ordet Esbjerg afhænger 100% af postnummeret 6700. Derfor er feltet Esbjerg overflødigt, og vi kan spare plads og besvær ved at splitte databasen op.

Link til toppen.

20.4 Kort eksempel på normalisering

Her er et eksempel, Du vil senere i bogen her få en mere grundig gennemgang af normalisering og relationer.

Kundedatabase på 0’de normalform

Her har jeg har bare noteret data ned.

Kundedatabase på 1. normalform

Alle tabeller skal have et felt til entydig identifikation af en post (dvs. en række). Dette felt kalder man en primærnøgle. Jeg har tilføjet et felt, KundeID, som er min primærnøgle.

Jeg vil gerne kunne sortere på kundernes fornavne og efternavne, så derfor deler jeg også KundeNavn op i KundeFornavn og KundeEfternavn. Jeg har ikke behov for at sortere på vejnavn og vejnummer i feltet Adresse, så dem beholder jeg, som de er.

Kundedatabase på 2. og 3. normalform

2. og 3. normalform går ud på det samme. Hvis nogle kolonner har gentagne data, så er de gentagne data overflødige. Dette kalder vi for “redundans”. Hvis der er redundans, så skal tabellen splittes op i 2. Postnummer forekommer flere steder. Bynavnet afhænger af postnummeret, så jeg splitter tabellen i 2.

I den nye tabel PostnummerBy bliver Postnummer primærnøgle. I den gamle tabel peger feltet Postnummer nu over på den nye tabel PostnummerBy, så den bliver fremmednøgle. Byernes navne findes nu kun 1 gang i databasen. Så tabellen med postnummer og by alene har kun 3 rækker/poster. Så vi har sparet 2/5 dvs. 40% af vores bynavne væk og gjort det nemmere at vedligeholde databasen.

Der kan være tekniske forskelle på, hvilken form for afhængighed kolonner have af hinanden, og derfor har man både en 2. og en 3. normalform. I praksis overlader vi forskellen til de virkelige nørder. Hvis du kan splitte tabellen op når det er nødvendigt, så er alt godt.

Link til toppen.

20.5 Flade kontra relationelle databaser og NoSQL

  • Hvis en database kun består af 1 tabel, så har vi en flad database
  • Hvis en database består af 2 eller flere forbundne tabeller, så har vi en relationsdatabase

Relationsdatabaser er den mest almindelige databaseform. Hvis du arbejder i f.eks. et ERP system, kan du være sikker på, at der ligger en relationsdatabase bag.

NoSQL databaser

Der findes en bruget forsamling af databaser, som man kalder for NoSQL databaser. Fælles for dem er, at de ikke bruger SQL. Eksempler er XML og Json databaser. XML ligner HTML, men man opfinder sine egne tags, og Json er en form for javaScript. Man laver ikke nødvendigvis normalisering og relationer mellem tabellerne i NoSQL databaser. Til gengæld kan man så bruge javaScript både frontend som normalt, backend gennem f.eks. NodeJS og som database hvis man bruger Json. Det kaldes full-stack, og full-stack udviklere bliver meget, meget gode til javaScript :-).

Et andet træk er ved NoSQL, at man ikke altid gør så meget ud af opbygningen af databasen. I sin mest primitive form laver man nogle gange bare 2 felter. Et id og et felt (kald feltet for “pulterkammer”), hvor alle andre data hældes usorteret ind. Det gør, at en maskine kan opdatere databasen selv. Til gengæld har den ikke samme faste struktur som en relationel database, og det gør det også noget vanskeligere at trække data ud af databasen. Men det er så programmørens problem.

NoSQL databaser og rationelle databaser udelukker på ingen måde hinanden. Her er en lille sammenligning mellem relationelle og NoSQL databaser.

EMNERELATIONELLE DATABASERNOSQL DATABASER
Opbygning– Omhyggelig opbygning gennem normalisering og relationer
– Store krav til input. Vi tillader ikke sammenblanding mellem f.eks. navn og alder
– Vi kan både have flade og relationelle dataser
– Skal bygges “i hånden”
– I sin mest primitive form kun 2 felter. et id og et “pulterkammer”felt, hvor alle data hældes i
– Kan opbygges relationelt, men det er på ingen måde et krav
– Kan opbygges af en maskine
Kodning– SQL – Alt andet end SQL, det afhænger af databasen
Output– Meget konservativ og forudsigelig– Ikke nødvendigvis særligt forudsigelig
Styrke– God til konservative databaser som f.eks. regnskaber, kunder og andet, hvor vi 100% skal kunne stole på output
– Konservativ og forudsigelig
– Nem at hente data ud af
– God til databaser, som maskiner skal kunne vedligeholde
– God til databaser, hvor output ikke behøves at være så præcist. Eksempel: Når du søger på Google, er det ikke den samme side, der nødvendigvis kommer øverst hver gang
– Eksplorativ. God at gå på opdagelse i
– Nem at oprette og vedligeholde
Svaghed– Processen med at oprette kan være tung
– Høje krav til data
– Kan være uforudsigelig, da der ikke altid er validering af input data
– Vi kan ikke være sikre på, at vi har registreret alt om alle. Vi kan f.eks. ikke være sikre på, at vi har efternavn, selvom vi har et fornavn. Det var aldrig sket i en relationel database

Vi skal ikke arbejde yderligere med NoSQL databaser i informatik B end i afsnittet her.

Link til toppen.

20.6 Sproget SQL og CRUD

SQL står for Structured Query Language. Hvis vi arbejder med relationelle databaser, hvad vi som oftest gør, så bruger vi altid SQL som sprog til at kommunikere med databasen.

SQL er forbløffende tæt på almindelig talesprog, og det er der en grund til. Det er IT-nørder, der har udviklet sproget. De ønskede, at selv økonomer skulle kunne lave deres forespørgsler ned i databasen selv uden at have brug for hjælp. Sådan gik det ikke, økonomerne fik nogle andre IT-nørder til at udvikle programmer, der kunne gøre det for dem. Men nu skal I lære SQL.

Der findes forskellige dialekter af SQL, f.eks. MS-SQL (MS står for “Microsoft”), MySQL m.fl. Kernen i de forskellige SQL dialekter er næsten éns, og du hopper fra den ene dialekt til lærer du den ene dialekt, kan du lynhurtigt lære den anden.

SQL kan grundlæggende kun 4 ting, som vi i daglig tale kalder CRUD. CRUD står for “Create, Read, Update og Delete. Skåret ind til benet, kan den ikke engang opdatere. Den sletter først og så skaber den en ny værdi bagefter.

HANDLINGKOMMANDOFORKLARING
CreateCREATEOpretter f.eks. en ny tabel eller nye data
ReadSELECTUdvælger data fra databasen. Du kan sige, at den foretager en filtrering. Den kan dog også sortere og beregne f.eks. sum og gennemsnit
UpdateUPDATEOpdaterer. Dvs. den opdaterer slet ikke. Det, den gør, er at den først sletter og så skaber en ny.
DeleteDELETESletter data fra databasen

Kommandoen SELECT er langt den vigtigste af de 4, så den bruger vi hele kapitel 21 Hent data med SELECT til. De øvrige er hurtige at gennemgå.

Link til toppen.

20.7 SQL sprogets syntaks

Et computersprogs syntaks = sprogets opbygning og “grammatik”.

Som du ser, så er SQL ret tæt på menneskesprog. Faktisk blev det skabt til, at økonomer selv skulle kunne finde ud af at programmere uden hjælp fra programmører. Det gik så ikke helt sådan, men her er et par regler:

  • Du bestemmer selv, om du vil skrive kommandoer med store eller små bogstaver. Jeg foretrækker store bogstaver lige modsat, hvad jeg gør i HTML og CSS. Bare du er konsekvent
  • Lad gerne nye kommandoer starte på en ny linje. Det er ikke et krav, men det gør det mere overskueligt
  • Nogle gange efterspørger SQL et semikolon efter en linje, dvs. inden en ny kommando
  • Pas på med navne, der ligner kommandoer. Et eksempel. SQL har kommandoen “BY”. Det vil vi danskere gerne bruge som feltnavn f.eks. ved siden af “Postnummer”. Hvis du har brugt et beskyttet navn, kan du komme rundt omkring det ved at sætte firkantet parantes rundt om. SELECT By FROM … vil give en fejl, mens SELECT [By] FROM … ikke giver fejl.
  • Du kan sætte tabelnavnet foran feltet og forbinde med en prik. F.eks. SELECT Customers.CustomerName, hvor tabelnavnet så står forrest. Det er nyttigt, når vi skal vælge data fra flere forskellige tabeller, men det kommer vi til

Hvad betyder “case sensitive”

Case sensitive betyder, at et kodesprog reagerer, hvis du bruger store bogstaver frem for små eller omvendt

HTML, CSS og SQL er ikke case sensitive. Det er ligegyldigt, om vi skriver “SELECT” eller “select” i SQL, “<HTML>” eller “<html>” i HTML

PHP og javaScript er case sensitive. Navne på variable og konstanter er f.eks. case sensitive i PHP. Variablen “$KUNDE” og “$Kunde” er ikke det samme som variablen “$kunde”

Link til toppen.

Kapitel 21 Hent data med SELECT

Først lidt teori. At hente data svarer til en filtrering. Hvis du har prøvet at filtrere i f.eks. Excel, så ved du, at der er helt utroligt mange måder at gøre det på. I SELECT har vi en række underkommandoer. Jeg præsenterer allerede nu rækkefølgen for dig, så du har set den. Lad dig ikke skræmme, senere skal du nok lære mere om de enkelte underpunkter og figuren vil efterhånden falde dig naturlig. Reglerne er simple:

  • Normalt vil du maksimalt anvende 2 – 4 af kommandoerne
  • Uanset hvor mange kommanduer, du bruger, skal kommandoerne skal altid være i den viste rækkefølge. F.eks. vil GROUP BY vil altid komme efter WHERE
  • Du kan genbruge figuren til de andre grundkommandoer i CRUD, dvs. CREATE, UPDATE og DELETE. Her vil dine kommandoer ofte være mere simple

Link til toppen.

21.1 Hent data med SELECT og FROM

De første kommandoer, du skal lære, er meget simple. Atter engang vil jeg bruge W3 schools. Som med HTML, CSS og PHP har de en komplet gennemgang af ikke kun SQL men også, hvordan man med PHP kan arbejde med en database og vise resultater frontend. Det første stump SQL er fra https://www.w3schools.com/sql/default.asp

SELECT * FROM Customers

Det er enormt simpelt. SELECT udvælger, “*” betyder “alt”, og “Customers” er navnet på den tabel, vi vælger data fra. “Udvælg alt fra tabellen Customers”. Det ser sådan her ud.

Select * from

Vælg bestemte kolonner

Hvis vi ønsker at hente bestemte kolonner ud frem for hele tabellen, så kan vi gøre det ret simpelt som du allerede ved:

SELECT * FROM Customers

Hvis vi ønsker at hente bestemte kolonner ud, så skriver vi bare kolonnernes navne i stedte for asteriks’en:

SELECT CustomerName, City FROM Customers;

Nu får vi kun vist felterne CustomerName og City

Select - where

Læs mere og test selv på https://www.w3schools.com/sql/sql_select.asp

SELECT med DISTINCT

Hvis jeg nu kunne tænke mig en liste over lande, som vi har kunder i, så kunne jeg gøre sådan her:

SELECT Country FROM Customers; 

Problemet er, at hvis f.eks. England forekommer mange gange i min liste. Det vil den gøre, hvis jeg har mange kunder i England. Men det er der råd for :-). Vi bruger bare koden DISTINCT

SELECT DISTINCT Country FROM Customers;

Så kommer det enkelte land kun ud 1 gang. Det ser sådan ud:

Læs mere og test selv på https://www.w3schools.com/sql/sql_distinct.asp.

Link til toppen.

21.2 SELECT med FROM og WHERE

Næste skridt er, at vi kan sætte en betingelse på. Prøv at se koden her og bemærk, at vi skriver den i 2 linjer. “WHERE” får sin egen linje, det gør koden mere overskuelig.

SELECT * FROM Customers
WHERE Country='Mexico';

Det, vi gør, er at vi først vælger alt fra tabellen Customers, men bagefter sætter vi en betingelse på. Betingelsen er, at i kolonnen Country skal landet være lig med “Mexico”. Det ser sådan ud:

Læs mere og test selv på https://www.w3schools.com/sql/trysql.asp?filename=trysql_select_where.

SELECT: AND, OR og NOT

AND. Du kan sikkert godt gætte, at nedenstående kommando vil give dig kunder fra Tyskland, der bor i Beriln:

SELECT * FROM Customers
WHERE Country='Germany' AND City='Berlin';

OR. Hvis vi nu i stedet skal have fra 2 byer, så er vi nødt til at bruge “OR”. Vores kunder kan jo ikke bo i 2 byer på en gang :-).

SELECT * FROM Customers
WHERE City='Berlin' OR City='München';

NOT. Hvis vi nu ønsker kunder fra alle andre lande end Tyskland:

SELECT * FROM Customers
WHERE NOT Country='Germany';

Du kan naturligvis kombinere AND, OR og NOT præcis som du vil. Læs mere og test selv på https://www.w3schools.com/sql/sql_and_or.asp.

Link til toppen.

21.3 SELECT med ORDER BY og ASC/DESC

Hvis vi ønsker en alfabetisk rækkefølge, så kan det selvfølgelig lade sig gøre. ORDER BY sørger for, at man kan sortere den valgte række i en bestemt orden

  • ASC sorterer fra A – Å
  • DESC sorterer fra Å – A

Se eksemplet nedenfor. Landene sorteres i faldende orden, mens CustomerName sorteres stigende inden for de enkelte lande. VIGTIGT: Hvis Country står først, så sorterer den Country først. CustomerName sorteres først når sorteringen af Country er overstået.

Læs mere og test løs på https://www.w3schools.com/sql/sql_orderby.asp.

Link til toppen.

21.4 SELECT med tæller, gennemsnit og sum

Der findes nogle funktioner i SQL, som kan lave nyttige udregninger for dig. Jeg vil gennemgå 3: COUNT, AVG og SUM. Det gælder for alle 3, at de selvfølgelig kan kombineres med andre SQL kommandoer. Eksemplerne er fra https://www.w3schools.com/sql/sql_count_avg_sum.asp.

Tæller

COUNT tæller antallet af rækker. Det kan se sådan ud:

SELECT COUNT(ProductID)
FROM Products;

I praksis.

Gennemsnit

AVG kan finde et gennemsnit i en række. Det kræver naturligvis, at rækken indeholder tal og ikke f.eks. tekst eller datoer. Koden kan se sådan ud:

SELECT AVG(Price)
FROM Products;

I praksis:

Sum

SUM kan lægge tal sammen i en række. Det kræver naturligvis lige som med gennemsnit (AVG), at rækken indeholder tal og ikke f.eks. tekst eller datoer. Det er yderst nyttigt. Forestil dig, at du f.eks. vil vide, hvor meget dine kunder i alt skylder dig, eller hvor mange stk. af en vare, du har ordrer på.Her er et eksempel:

SELECT SUM(Quantity)
FROM OrderDetails;

Det ser sådan ud i praksis:

Link til toppen.

21.5 SELECT med LIKE

LIKE bruger man typisk til at søge efter tekst, men man kombinerer det meget ofte med såkaldte wildcards. Her er et eksempel:

SELECT * FROM Customers
WHERE CustomerName LIKE 'a%'; 

Den første linje vælger alt fra tabellen Customers. Den anden linje udvælger kunder, der starter med “a”. Når man sætter et procenttegn efter, så siger vi, at vi er ligeglade med, hvad der kommer efter a’et. Denne kalder man et wildcard.

Et eksempel mere. Med wildcards både foran og efter “or”, så finder vi ethvert ord, der indeholde “or”. Det gælder, uanset om vi har andre bogstaver før eller efter.

SELECT * FROM Customers
WHERE CustomerName LIKE '%or%';

Den sidste ser sådan ud. Bemærk at bogstavkombinationen “or” indgår i alle de udvalgte felter i CustomerName

Man kan lave meget mere med wildcards, læs mere på https://www.w3schools.com/sql/sql_like.asp.

Link til toppen.

21.6 SELECT med GROUP BY og HAVING

GROUP BY

GROUP BY kan sortere data, dvs. rækker, i grupper. Vi kombinere den ofte med såkaldte aggregat-funktioner. Aggregat funktioner er funktioner, der kan “samle” noget, for “aggregering” betyder “at samle”. Eksempler på aggregat funktioner kan være funktioner som f.eks. COUNT, SUM, AVG, MIN eller MAX.

Når man gør det, så kan man komme frem til noget ret kraftfuldt. Her er et eksempel:

SELECT COUNT(CustomerID), Country
FROM Customers
GROUP BY Country;

Funktionen vil så tælle, hvor mange rækker (i det her tilfælde kunder), der er for hver gang kolonnen Country er noget forskellig. Det vil sige, at vi får antal kunder pr. land. Der er 3 kunder i Argentina, 2 i Austria (Østrig) osv.

SQL group by

Bemærk: Den kolonne, vi bruger under COUNT, kan være hvilken som helst kolonne i tabellen. Hvis du erstatter koden COUNT(CustomerID) med koden COUNT(CustomerName) eller endda COUNT(*), så vil det give samme resultat. Læs mere og test af på https://www.w3schools.com/sql/sql_groupby.asp

HAVING

Vi bruger WHERE til at angive en filtrering.

SELECT * FROM Customers
WHERE Country=’Mexico’;

Når vi arbejder med GROUP BY, kan vi også få brug for en filtrering. I stedet for at bruge WHERE, så bruger vi HAVING. Her er et eksempel

SELECT COUNT(CustomerID), Country
FROM Customers
GROUP BY Country
HAVING COUNT(CustomerID) > 5
ORDER BY COUNT(CustomerID) DESC;

Vi vælger antal kunder grupperet på lande. Vi vil dog kun se de lande, hvor der er mere en 5 kunder. Den sidste sætning ORDER BY har du set før, den bruger vi til at sortere antal kunder pr. land i faldende orden.

SQL - having

Læs mere og test selv af på https://www.w3schools.com/sql/sql_having.asp.

Link til toppen.

21.7 Hent data fra flere tabeller med SELECT og JOIN

I afsnit 20.3 Normalisering og det efterfølgende afsnit 20.4 Kort eksempel på normalisering viser jeg, hvordan og hvorfor man ofte splitter databasen op i flere tabeler. Når vi splitter op, skal vi sørge for et system af primær- og fremmednøgler, men det gennemgår jeg i detaljer i næste kapitel.

Opsplitningen så sådan ud:

3. normalform

Men. Selvom vi splitter tabellen op, skal vi selvfølgelig stadig kunne hente alle data. Det gælder, uanset hvilke tabeller vores data befinder os i. Hvis vi vil hente data på tværs af tabeller, så bruger vi kommandoen JOIN. Her er basis eksemplet fra W3 Schools:

SELECT Orders.OrderID, Customers.CustomerName
FROM Orders
INNER JOIN Customers ON Orders.CustomerID = Customers.CustomerID;

Forstå koden

KODEFORSTÅ KODEN
SELECT Orders.OrderID, Customers.CustomerName, Orders.OrderDateSELECT udvælger som altid. Orders.
OrderID er ny. Der står egentlig “tabel.kolonne”, så Orders = tabellen og Order.ID er en kolonne i den tabel, vi har valgt. På den måde kan vi vælge kolonner fra flere forskellige tabeller
FROM OrdersHer skal vi angive den ene af de 2 gabeller, her Orders
INNER JOIN Customers ON Orders.CustomerID=Customers.CustomerID;Her forbinder fra den anden tabel Customers til Orders. Vi forbinder primærnøglen med fremmednøglen eller omvendt. Bare der er 2 kolonner i hver tabel, som svarer til hinanden. I min opsplitning øverst i afsnittet forekom kolonnen “Postnummer” i begge tabeller, og det er præcis, hvad jeg udnytter her.

Det ser sådan i praksis. Bemærk at vi har kolonner fra 2 forskellige tabeller.

Link til toppen.

21.8 JOIN i mit eksempel med kundedatabasen

I mit eksempel med kundedatabasen, så endte jeg med 2 tabeller. Lad os kalde de 2 tabeller for hhv. Kunde og PostnummerBy.

Tabellen PostnummerBy har Postnummer som primærnøgle, og tabellen Kunde har Postnummer som fremmednøgle. De 2 kan vi bruge til at hente oplysninger i de 2 tabeller. Jeg ønsker nu KundeFornavn, KundeEfternavn, Postnummer og By. Koden bliver:

SELECT Kunde.KundeFornavn, Kunde.KundeEfternavn, Kunde.Postnummer, PostnummerBy.By
FROM Kunde 
INNER JOIN PostnummerBy ON Kunde.Postnummer = PostnummerBy.Postnummer;

Forstå koden

KODEFORSTÅ KODEN
SELECT Kunde.KundeFornavn, Kunde.KundeEfternavn, Kunde.Postnummer, PostnummerBy.BySELECT udvælger. Igen bruger jeg notationen tabelnavn.kolonne for at hente kolonnerne fra de forskellige tabeller. Kunde.KundeFornavn leder i tabellen “Kunde” efter kolonnen “KundeFornavn”
FROM KundeHer skal vi angive den ene af de 2 gabeller, her Kunde
INNER JOIN PostnummerBy ON Kunde.Postnummer = PostnummerBy.Postnummer;Her forbinder fra den anden tabel Kunde til PostnummerBy. Vi forbinder primærnøglen med fremmednøglen eller omvendt. Bare der er 2 kolonner i hver tabel, som svarer til hinanden, og her er det kolonnen Postnummer, der findes i begge tabeller. Bemærk dog, at primærnøglen og fremmednøglen ikke behøves at have samme navn, bare de har samme format (tekst, heltal, nummer eller lignende).

Resultatet af min kode. Jeg har anvendt Access som databaseprogram.

Afslutning: JOIN er en super vigtig kommando. Den er lidt svær at forstå, men det lønner sig. Den åbner døre til, at vi kan trække data på kryds og tværs af vores databaser. I næste afsnit fortæller jeg lidt om forskellige typer JOIN, for INNER JOIN er bare en af flere forskellige slags JOIN kommandoer.

Link til toppen.

21.9 Forskellige typer JOIN

I sidste afsnit viste jeg, hvordan du kan hente fælles rækker fra 2 tabeller i samme database med kommandoen INNER JOIN. Der findes flere typer JOIN. Her er et par stykker:


Du kender INNER JOIN. Jeg har fundet nogle andre JOIN til dig. LEFT OUTER JOIN tager hele venstre tabel, og der findes selvfølgelig også en tilsvarende RIGHT OUTER JOIN. I nogle typer databaser skriver man bare LEFT hhv. RIGHT JOIN. FULL OUTER JOIN tager alle data fra begge tabeller, og den hedder også nogle gange bare FULL JOIN. SELF JOIN joiner tabellen med sig selv.

KOMMANDOHENVISNING TIL W3SCHOOLS
INNER JOINhttps://www.w3schools.com/sql/sql_join_inner.asp
LEFT JOIN/LEFT OUTER JOINhttps://www.w3schools.com/sql/sql_join_left.asp
RIGHT JOIN/RIGHT OUTER JOINhttps://www.w3schools.com/sql/sql_join_right.asp
FULL JOIN/FULL OUTER JOINhttps://www.w3schools.com/sql/sql_join_full.asp
SELV JOINhttps://www.w3schools.com/sql/sql_join_self.asp

Link til toppen.

21.10 Hackning med SQL injections

Du ved nu, at man bruger SQL til at hente informationer ud af databaser med. Du ved også, at man kun kan 4 ting med en database: Create, Read, Update og Delete (CRUD).

SQL kommandoen “DROP DATABASE testDB” vil slette hele databasen “testDB”.

Formularer og søgefelter giver brugeren adgang til at arbejde med databasen. Hvis en bruger er ondsindet, kan brugeren forsøge at slette databasen ved at sætte SQL kode ind i et felt. Her har jeg sat kode ind i søgefeltet på hjemmesiden her:

Hvis man ikke passer på, så vil en bruger kunne slette min database på den her måde. Det kaldes SQLInjections, og det er en helt almindelig form for hackning.

Link til toppen af siden.

Kapitel 22. CRUD kommandoer

Du ved, at vi i en database kun kan oprette (create), læse (read), opdatere (update) og slette (delete) data. Det forkorter vi til CRUD. I kapitlet her skal vi se på kommandoer til at indsætte, slette, opdatere og oprette tabeller.

Tag ikke fejl af SQL. CRUD er simpelt, men SQL er et komplet sprog, og alle de gængse ting i et sprog som variable, sekvens, forgrening-betingelse, arrays og loops, kommentarer og endda funktioner og events finder du også i SQL. Eneste forskel er, at events hedder “triggers”. SQL har også meget omfattende muligheder for beregning og dataanalyse. Du har set SUM og AVG (gennemsnit), men du kan f.eks. også lave en regression.

I kapitel 21 Hent data med SELECT så vi på læse/read-delen af CRUD. Den er langt den mest omfattende af de 4. I kapitlet her ser vi på de resterende 3 dele. Opdatering, sletning og til sidst at oprette data.

Link til toppen.

22.1 Indsæt data med SQL kommandoen INSERT INTO

I virkeligheden findes kommandoen UPDATE slet ikke. Der sker det, at SQL først sletter data og derefter opretter data. Men som brugere tror vi, at der sker en opdatering. Kommandoen er simpel:

INSERT INTO: Først skriver du, hvilken tabel du vil indsætte i . I eksemplet er det “Customers”. Bagefter skriver du i parentes de kolonner, som du vil have værdier indsat i. Her er det kolonnerne CustomerName, ContactName osv. Du bestemmer selv, hvor mange kolonner du vil indsætte data i. Der er intet krav om, at du skal indsætte i alle kolonner hver gang.

VALUES: “Values” betyder “værdier”. Du lister de værdier, som skal indsættes i tabellen. Her værdierne “Cardinal”, “Tom B. Erichsen” osv.

Alt i alt såre simpelt. Med INSERT INTO fortæller du først i hvilken tabel og i hvilke kolonner, du skal indsætte værdierne. Med VALUES fortæller du, hvilke specifikke værdier, du skal indsætte. Læs mere på https://www.w3schools.com/sql/sql_insert.asp.

Link til toppen.

22.2 Indsæt data med SQL kommandoen UPDATE

I virkeligheden findes kommandoen UPDATE slet ikke. Der sker det, at SQL først sletter data og derefter opretter data. Med andre ord: Kommandoerne DELETE efterfulgt af INSERT INTO kan klare jobbet alene. Men som brugere tror vi, at der sker en opdatering, og programmører er dovne. De foretrækker 1 frem for 2 kommandoer. Selve kommandoen er simpel:

Update

UPDATE. Update fortæller, hvilken tabel vi vil arbejde med. Her tabellen “Customers”

SET fortæller, hvilke kolonner vi vil opdatere og med hvilke værdier. I eksemplet bliver rækken “ContactName” opdateret til “Alfred Schmidt” og rækken “City” bliver opdateret til “Frankfurt”.

WHERE fortæller, at opdateringen skal ske i den række, hvor CustomerID = 1. Customer ID er selvfølgelig primærnøglen i tabellen. Læs mere på https://www.w3schools.com/sql/sql_update.asp.

Husk altid at bruge WHERE når du bruger UPDATE

Du kan komme til at lave en masse ballade, hvis du glemmer sætningen WHERE. Hvis den ikke er med, så vil alle rækker blive opdateret med det, der står i sætningen SET. I eksemplet ovenfor vil alle rækkerne i kolonnerne ContactName og City blive opdateret med hhv. “Alfred Schmidt” og “Frankfurt”, hvis du glemmer kommandoen “WHERE CustomerID = 1;”

Link til toppen.

22.3 Slet data med SQL kommandoen DELETE

Du kan slette i en databaser ved at bruge kommandoen her:

SQL Delete

DELETE FROM Customers. Kommandoen siger, at vi vil slette i tabellen “Customers”.

WHERE CustomerName = “Alfreds Futterkiste” sletter den række, hvor feltet CustomerName = “Alfreds Futterkiste”. Læs mere på https://www.w3schools.com/sql/sql_delete.asp.

Husk altid at bruge WHERE også når du bruger DELETE

Hvis du glemmer WHERE, så vil du bare have sætningen DELETE FROM Customers tilbage. Hvis du kører den kommando, så sletter du alt indhold i tabellen Customers

Link til toppen.

22.4 Vigtigt: Vi vil helst ikke slette i databaser

Lad os tage et eksempel. Du har en kunde, “Tommy Træls”. Han brokker sig altid, og han betaler ikke sine regninger. Du ringer til Tommy og spørger, hvorfor han ikke betaler. I stedet for at undskylde, så brokker han sig og siger, at dine varer aldrig kommer til tiden og at de er dårlige, og det vil han f…. og k… edeme (bandeord) ikke betale for. Du siger til ham, at så må han købe hans varer et andet sted, og at du sender ham til inkasso. Slut med at sælge til Tommy.

Men – sletter vi ham så fra vores kundedatabase? Nej, det kunne vi aldrig finde på. Selvom han ikke er kunde hos os mere, så er informationerne om ham vigtige. Han har gæld til os, og hvis han ringer en anden gang, så kan vi slå ham op og sige, at vi ikke vil sælge til ham. I stedet vil vi typisk have et felt, Status som en fald-ned menu, hvor vi kan vælge forskellige typer status. Aktiv, passiv, blokeret eller hvilken status vi nu vælger.

Indenfor regnskaber er det regulært forbudt at slette. Hvis vi laver en fejlpostering, så laver man en tilbage- eller ompostering, men vi sletter aldrig. Ikke engang hvis fejlen er åbenlys. Det sker for at undgå snyd med regnskaber.

Link til toppen.

22.5 Kort overblik over vores daglige CRUD kommandoer

Her er en meget lille og enkel kundedatabase. Tabellen kalder vi for “Kunde”

Hvis vi skal bruge kommandoerne INSERT, SELECT, UPDATE og DELETE på tabellen Kunde, så kunne det f.eks. sådan ud

KOMMANDOEKSEMPELFORKLARING
INSERT INSERT INTO Kunde(KundeID, Postnummer, By, Aktiv/passiv)
VALUES (6, “Niels Ny”, 6000, “Kolding”, “Aktiv”
Opretter en ny række
med værdier
SELECTSELECT *
FROM Kunde
Henter alle data fra
tabellen Kunde
UPDATEUPDATE Kunde
SET Postnummer = 6000, By = “Kolding”
WHERE KundeNavn = “Viggo Vare”
Viggo er flyttet til Kolding,
så vi opdaterer hans
postnummer og by.
Husk at
bruge WHERE, ellers opdaterer
du alle rækker
DELETEDELETE FROM Kunde
WHERE Kundenavn = “Tommy Træls”
Vi sletter kunden Tommy Træls.
Husk kommandoen WHERE,
ellers sletter du alt i tabellen

Link til toppen.

22.6 RDBMS = databaseprogrammer

Du ved, at WordPress er et “CMS” system. CMS står for “Content Management System”. CMS er en fællesbetegnelse for systemer, som vi kan bruge til at styre indholdet på en hjemmeside.

RDBMS står for “Relational Database Management System”. Det er en helt tilsvarende betegnelse bare for programmer, som vi kan bruge til at styre relationelle databaser. Bemærk, at betegnelsen RDBMS ikke dækker programmer til at styre NoSQL-databaser. Her er en lille oversigt over de mest almindelige

PROGRAMFORKLARING
Microsoft AccessEn del af Office-pakken
Super super brugervenlig, men fungerer ikke på Mac
Professionelle udviklere rynker lidt på næsten af den. De kalder den for en “husmands-database” underforstået at den er lavet til ikke-programmører. Det sidste er rigtigt, men det betyder så, at f.eks. økonomer kan bruge den. Excel og Access arbejder fantastisk sammen
OracleTop-prof men også kæmpestor. Nok den mest populære af alle
MySQLMySQL hører typisk sammen med PHP. De er begge det stærkeste bud fra open-source-verdenen, og vi skal arbejde med MySQL i faget. WordPress kører på en MySQL database. MySQL er open-source. Måske lidt primitiv brugerflade, men den er virkelig hurtig.
Oracle er med i MySQL databasen.
Microsoft SQL ServerMicrosofts “professionelle” database, dvs. modsat Access rettet mod ikke almindelige brugere, men programmører
SQLiteLille, nem, gratis og effektiv database, som vi også skal arbejde med i opgaverne

Link til toppen.

22.7 Datatyper

En datatype er det samme som hvilken slags data, vi arbejder med. Du kender det allerede fra f.eks. Excel. Der er forskel på, om vi arbejder med tekst, tal, heltal, datoer eller … Hvis du f.eks. skriver “27-okt-2020” i en celle i Excel, så laver Excel det øjeblikkeligt om til et datoformat.

Det er rigtigt praktisk, at vi på forhånd kender formatet, for så kan vi bedre arbejde med vores data. At trække 2 tal fra hinanden og finde forskellen er slet ikke det samme som at trække 2 datoer fra hinanden og finde forskellen i antal dage.

I Excel retter vi typisk formatet til hen ad vejen, hvis vi f.eks. vil arbejde med datoer eller vil have et beløb vist med “kr” eller “€” bagved. I den type databaser, som vi arbejder med her, skal vi beslutte vi os for datatypen på forhånd. Her er et overblik over de mest almindelige datatyper.

DATATYPEFORKLARING
INTEGERHeltal. Vi bruger den naturligvis til heltal, men vi kan f.eks. også bruge den til primærnøgler. Vi kan gøre det sådan, så hver ny række automatisk får nummeret fra rækken ovenfor plus 1.
REALReal er også et tal, men her tillader vi alle mulige tal. Dvs. også kommatal f.eks. -123,456
TEXT Text er naturligvis tekst. Det er almindeligt, at man deler op i VARCHAR() og TEXT. VARCHAR er en afgrænset mængde tekst, f.eks. et efternavn. VARCHAR(50) vil sige, at vi højest tillader, at feltet bliver 50 karakterer langt. Hvis vi omvendt har behov for fritekst uden begrænsninger, så bruger vi bare TEXT
DATO OG TIDLige som i Excel kan vi registrere datoer og f.eks. beregne antal dage mellem 2 datoer eller beregne antal timer mellem 2 tidspunkter
BOOLEANEn BOOLEAN er en datatype, der kun kan rumme 2 værdier. Ja/nej, sandt/falsk osv. I nogle tilfælde kan dog også rumme NULL, dvs. ikke-udfyldt.
BLOBBLOB står for Binary Large OBject. Det kan være alt muligt ikke-indtasteligt, typisk billeder, video eller andet multimedie-lignende

De enkelte database-programmer har hver deres måde at håndtere datatyper på. Her er et eksempel fra den meget brugervenlige Microsoft Access. De er endda oversat til dansk!

  • Kort og lang tekst svarer til VARCHAR() og TEXT
  • Tal er enten en INTEGER eller en REAL. Det samme gælder Stort tal og Valuta
  • Autonummerering er en INTEGER. Det svarer til at man i en primærnøgle tager sidste række og lægger 1 til, så vi får et unikt nummer. Valuta,
  • Ja/nej er en BOOL
  • OLE-objekt, Link og Vedhæftet fil er alle BLOB-objekter

Skal cpr-, telefon, og postnumre være tal eller tekst

Hvis det giver mening at lægge 2 tal sammen, så skal det være et tal. Hvis det er et specielt format, så er tekst bedst:

  • CPR nummer er et specialformat: 010203-4567. Det skal være tekst
  • Telefonnummer er også et special format: 12 34 56 57. Det skal også være tekst
  • Postnummer: Vi kan klare os med tal. Men hvis vi får engelske postnumre, så bruger de desværre tekst i deres postnumre

Bemærk, at det hverken giver mening at lægge CPR-, telefon eller postnumre sammen.

Link til toppen.

22.8 Opret data med SQL kommandoen CREATE

I de mest brugervenlige databaser kan vi sagtens oprette databaser og tabeller uden at kode. Vi kommer dog ikke udenom, at kodning kan gøre tingene noget hurtigere end vi kan gøre i programmet. Især når vi taler om store datamængder med mange kolonner.

I eksemplet nedenfor er jeg i gang med at oprette en tabel, “Kunder” i SQLite. Midt i vinduet definerer jeg, hvordan kolonnerne og datatyperne skal se ud. Bemærk, at SQLite samtidig skriver koden nedenunder.

Skab tabel i SQLite
  • CREATE TABLE Kunder opretter selvfølgelig tabellen “Kunder”
  • Indrykket bliver de enkelte kolonner oprettet. Hver får et navn og en datatype. F.eks. får “KundeID” datatypen INTEGER, mens “Fornavn” får datatypen TEXT.
  • Bemærk nederst. PRIMARY KEY (“KundeID”) definerer feltet “KundeID” som primærnøgle

Link til toppen.

22.9 Import og eksport af data med CSV formatet

.csv er et meget brugt format, når vi snakker databaser. Du kan sige, at .csv er en slags “fladtrådt” Excel fil. Excel filer kan være særdeles komplicerede med formler, grafik og flere faneblade. Excel formatet kan stort set kun læses af Excel og andre Office programmer plus visse ERP systemer.

Men lige som vi i GIMP og PhotoShop har både arbejdsformater og eksport-formater, så har vi også et godt eksportformat i Excel. Vi har det meget brugte .csv format.

Her er et lille eksempel. Jeg har lavet en lille tabel i Excel over varer.

CSV format 1

Den vil umiddelbart være meget svær at eksportere. Derfor findes der et mere primitivt format, som er meget benyttet. Jeg gemmer filen i .csv formatet:

CSV format 2

Nu åbner jeg .csv formatet. Nu er det ren tekst, som jeg kan åbne i en notesblok. Værdierne er organiseret i rækker og adskilt af semikolon. Csv står for “comma-separated values”. De er ekstremt nemme både at importere og eksportere data i.

Du kan f.eks. hente data om virksomheder hos Krak, data om den økonomiske udvikling hos Nationalbanken, data om verdens udvikling hos Verdensbanken, IMF og selv det amerikanske efterretningsvæsen CIA har en stor databank, hvor du kan hente data. Alt sammen i .csv formatet.

Link til toppen.

22.10 Excel versus databaser

Der er en god og fornuftig arbejdsdeling mellem Excel og databaser, som desværre ikke altid bliver overholdt. Excel er et fantastisk program til at bearbejde data i, men det er ikke velegnet til at opbevare data i. Rigtigt mange firmaer opbevarer vigtige data i Excel, men det er desværre super nemt både at slette og især at ændre data i Excel. Data er ubeskyttede.

Anderledes er det med databaser. Her er data godt beskyttede og godt opbevaret. Til gengæld er databaser ikke særligt gode til dataanalyse, selvom det kan lade sig gøre.

Så: Økonomer elsker Excel. Det gælder alle, fra de tungeste dataanalyse-folk til marketing folk og deres salgsstatistikker og kasseren hos de lokale spejdere. Bare lad være med at opbevare vigtige data i Excel, det skal du bruge databaser til.

Link til toppen.

Kapitel 23. Skab en database

Du ved allerede meget om, hvad en database er, og hvordan du kan bruge SQL til at kommunikere med databasen.

I kapitlet her skal vi se på, hvordan man kommer fra et blankt stykke papir og til en funktionsdygtig database. Der er nogle trin, som man skal gå igennem, for at nå fra de første notater og til en færdig database.

Du vil også lære, hvorfor den type database, vi arbejder med, kaldes en relationsdatabase. Man behøves ikke at bygge en database som relationsdatabase, der findes andre typer databaser f.eks. NoSQL databaser. Relationelle databaser er langt de mest almindelige, og databaser som CPR registret, skattevæsen og databaser bag regnskabssystemer er alle relationelle databaser. Se evt. afsnit 20.5 Flade kontra relatonelle databaser og NoSQL.

I kapitlet får du et gennemgående eksempel, nemlig en database over biler og bilejere.

Link til toppen.

23.1 Start med en brainstorm over ønsker til databasen

Første skridt er at lave en liste over, hvilke oplysninger vi overhovedet kunne tænke os at have med i databasen. Listen bliver senere til vores tabeller og poster i databasen. I første runde drejer det sig dels om at få et overblik over, hvad vi gerne vil have med, dels om at få grundlaget for at skabe databasens struktur

Eksempel med biler og bilejere

Her er en liste over, hvad vi godt kunne tænke os at gemme i en database over biler og bilejere.

  • BIlejer
  • Fulde navn
  • Adresse
  • Postnummer og by
  • Telefonnummer
  • Bil
  • Bilmærke
  • Biltype (personbil eller stationcar)
  • Bil årgang
  • Nummerplade
  • Email
  • Kørekørt nummer
  • Bilens stelnummer

Link til toppen.

23.2 Entiteter, relationer og attributter

For at kunne arbejde fornuftigt med en database skal vi lave et E-R diagram over de ønsker, vi har til databasen.

  • E står for “entitet”. Entiteten eller “enheden” bliver til databasens tabeller
  • R står for “relation”
  • Entiteter er de overordnede begreber. F.eks. en kunde eller et medlem. De bliver til navne på tabellerne
  • Relation er det forhold, de har til hinanden. Dem gemmer vi til afsnit 23.5 Håndtering af relationer.

Et sidste begreb, du skal kende, er attributter. En attribut er en egenskab ved en entitet. En egenskab ved en kunde kunne være navn, adresse, postnummer og by, varekøb mv.

Man kan sige, at entiteter er det overordnede begreb, parablyen, som attributterne så hører under. I tegningen har jeg indsat et par attributter fra bilejere og biler:

Eksempel med biler og bilejere: Entiteter og attributter

Lad os kikke på listen fra afsnit 21. Vi vil kikke efter, hvad der kunne være entiteter, og hvad der kunne være attributter. Entiteten er de overordnede begreber, og attributterne er egenskaber ved entiteterne.

Umiddelbart er der 2 entiteter. “Bil” og “Bilejer”. Alle andre egenskaber kan vi placere under de 2 entiteter. Listen kommer nu til at se sådan ud.

  • Bil
    • Bilmærke
    • Biltype (personbil eller stationcar)
    • Bil årgang
    • Nummerplade
    • Bilens stelnummer
  • BIlejer
    • Fulde navn
    • Adresse
    • Postnummer og by
    • Telefonnummer
    • Email
    • Kørekørt nummer

I dette tilfælde var vi heldige. Vi fandt både entiteter og attributter, og vi kunne organisere alle attributter under entiteterne. Hvis vi IKKE kan det, så må man tilføje ekstra entiteter til listen.

Link til toppen.

23.3 Afbildning af entiteter og relationer i et E-R diagram

Jeg fortsætter med eksemplet med bil og bilejer: Lad os starte med entiteterne. Dem afbilder vi som 2 kasser med en diamant imellem. Diamanten symboliserer en relation. Dvs. der er er en sammenhæng mellem entiteterne “Bilejer” og “Bil”. Den sammenhæng er, at “Bilejer” “ejer” “Bilen”, og den sammenhæng skriver jeg i diamanten. Der gælder også den sammenhæng, at “Bil” “ejes af” Bilejer”.

E-R diagram

Tip til dig, hvis du selv skal lave E-R diagrammer

Du kan lave E-R diagrammer i mange programmer, selv i PowerPoint. Jeg har tegnet alle diagrammer i et gratis online program, der hedder Lucidchart. Jeg vil anbefale dig at bruge dette program, hvis du skal lave E-R diagrammer

Nu har vi forbundet entiteterne med en relation. Men vi skal finde ud af, hvilken relation der så er tale om.

Måden, vi finder ud af det på, er ved at fuldføre sætningerne her. Sætningerne er bygget udelukkende ud fra diagrammet ovenfor

  • En bilejer kan eje EN/MANGE biler
  • En bil kan ejes af EN/MANGE bilejere

Hvis vi fuldfører sætningerne, så står der:

  • En bilejer kan eje MANGE biler
  • En bil kan ejes af EN bilejere

Vi har altså en en-til-mange relation. Det skriver vi ind i vores diagram: Bemærk hvordan vi skriver, relationerne skrives der, hvor vi gør hen imod. “M” står henne ved “Bil”, fordi “1 bilejer ejer mange biler” (1). Omvendt ejes en bil af en bilejer(2).

E-R diagram

Vigtig regel om entiteter

En entitet kan være forbundet med et ubegrænset antal andre entiteter, men den må aldrig stå alene. Dvs. den skal være forbundet til mindst 1 anden entitet

Link til toppen.

23.4 E-R diagrammer også med attributter

Vi kan også sætte attributter på. Vi afbilder attributter som bobler med streger ind til entiteten.

Eksemplet med biler og bilejere ser nu sådan ud:

Eksemplet fortsættes i afsnit 23.6 Normalisering. Vi skal lige igennem noget teori omkring relationer først i de næste par afsnit først.

Link til toppen.

23.5 Relationer

Der findes 3 typer relationer mellem entiteter: 1-til-1, 1-til-mange og mange-til-mange.

RELATIONFORKLARING
1:1 .
Udtales “En-til-en”.
1 person “A” er gift med 1 anden person “B”, og “B” kan også kun være gift med en “A”
1:m.
Udtales “En-til-mange”.
1 person kan have postadresse på 1 postnummer, men 1 postnummer kan godt have mange personer tilknyttet
m:m.
Udtales “mange-til-mange”
1 vare kan købes af mange kunder, og 1 kunde kan købe mange varer

1-til-1 relationen

1:1 relationer er ikke så udbredte. Vi kan vælge at håndtere den på 2 måder. Enten ved at slå de 2 entiteter sammen eller ved at holde dem adskilte.

Hvis vi vælger at holde dem adskilte, skal vi på en eller anden måde sørge for, at den ene er forbundet til den anden.

Du ved fra normalisering, at alle tabeller skal have en primærnøgle. Hvis der ikke er en naturlig primærnøgle, så må vi lave en. F.eks. er det normalt, at man tilføjer et kundeID til en kunde.

Jeg anerkender og glædes ved, at der findes masser af homoseksuelle par. Men for overskuelighedens skyld antager jeg i eksemplet her, at vi har 2 entiter. Kvinder og mænd. En kvinde kan være gift med en mand og omvendt, men de kan ikke være gift med en person af samme køn. Der findes ikke en naturlig, unik attribut (vi siger, at vi ikke har adgang til et CPR nummer), som vi kan bruge som primærnøgle. Derfor tilføjer jeg et kvindeID og et mandeID som attributter, som jeg samtidig kan bruge som primærnøgler.

I E-R diagrammer understreger man attributter, som er primærnøgler. Et E-R diagram over mænd og kvinder ser nu sådan ud. Jeg har udeladt alle andre attributter end primærnøglerne

Måden vi forbinder de 2 entiteter på, er ved at tage den ene entitets primærnøgle og sætte den ind hos den anden. Ved en 1:1 relation er det ligegyldigt, hvilken entitet vi tager primærnøglen fra. Lad os være galante og flytte mandens ID over til kvindens som en ny attribut. Den nye attribut bliver til fremmednøgle i den tabel, vi sætter den ind i. Resultatet bliver:

Nu er de 2 entiteter forbundne, og alt er godt!

1-til-mange relationen

I en en-til-mange relation gør vi det samme som ovenfor, men her er det ikke ligegyldigt, hvilken tabel vi tager primærnøglen fra.

Et klassisk eksempel: Vi har en entitet (tabel) med kunder: Attributterne er Kunde-id, Navn, Adresse, Postnummer og By. Da By afhænger 100% af Postnummer, skal de 2 deles ud i en ny entitet Postnummer_by

  • En kunde har ET postnummer
  • Et postnummer haves af MANGE kunder

Her er det nødvendigt at sætte primærnøglen fra dem, der kun er en af, over hos dem, der er MANGE af. Tabellen Postnummer_by har primærnøglen Postnummer. Derfor skal attributten postnummer også være i entiteten Kunde, hvor den bliver fremmednøgle. E-R diagrammet bliver som vist:

En-til-mange relation

Mange-til-mange relationen

En mange-til-mange relation er helt normal. Her er et eksempel: En kunde køber mange varer, og en vare kan købes af mange kunder. Det ser sådan her ud. Jeg har udeladt alle andre attibutter end primærnøglerne.

Sjovt nok kan vi slet ikke modellere mange-til-mange relationer i vores database. Det, som vi kan gøre, er i stedet at bryde den op i 2 1-til-mange relationer. Vi laver en ny entitet, som jeg normalt ville give et blandingsnavn mellem kunde og vare, f.eks. Kunde_vare.

I dette tilfælde har entiteten et navn, nemlig “Faktura”. Faktura har 1:mange relation til både Kunde og Vare. Primærnøglerne fra hhv. Kunde og Vare skal indgå i den nye tabel som fremmednøgler. Samtidig tilføjer jeg et id til den nye tabel. Relationerne bliver

  • EN kunde kan være på MANGE fakturaer
  • EN faktura kan have EN kunde

Med varer antager vi for nemheds skyld, at kunden kun kan købe en vare pr faktura. Hvis ikke, så har vi en ny mange-til-mange relation, som vi skal arbejde med. I virkeligheden ville det svare til fakturalinjer på en faktura.

  • EN vare kan være på MANGE fakturaer
  • EN faktura kan have EN vare

Med andre ord: Vi kan nu gøre præcis som før under 1: mange relationen. Vi skaber en forbindelse fra Kunde og Vare ved at sætte entitetens primærnøgle (Kunde-id og Vare-id) ind i den entitet, som den har en MANGE-relation til. Her er det Faktura.

Hvad så med primærnøglen i den nye tabel

Vi har 2 muligheder.

  • Vi kan lave den nye primærnøgle som en “sammensat” nøgle. Dvs. som en kombination af de 2 attributter Kunde-id og Vare-id. De er så begge 2 primærnøgler
  • Eller vi kan lave en ny, selvstændig primærnøgle, som vi kalder for Faktura-id.

Jeg foretrækker klart den sidste løsning, som er mere simpel, men begge dele virker.

Link til toppen.

23.6 Begreber inden for normalisering

Du kender allerede begreber inden for normalisering. Vi bruger normalisering for at sikre en database, der er nem at opdatere og er slankest mulig. Vi skal altid normalisere vores databaser inden, at vi begynder for alvor at lægge data ind i dem eller lave SQL.

Primærnøgle og fremmednøgle.

En primærnøgle er en unik værdi ved en post. F.eks. har vi danskere CPR nummer som unik værdi. Alle poster skal have en unik værdi, ellers kan vi ikke kende forskel. Hvis man ikke har en naturlig, unik værdi, så tilføjer vi bare en. Det kan f.eks. være “Kundenummer” eller “Kunde-id” til kunder.

Når vi refererer (dvs. indsætter) en primærnøgle i en anden tabel for at skabe en relation, så kaldes den indsatte værdi en fremmednøgle.

Inkonsistens

Inkonsistens er, når vi ikke kan stole på vores data. Hvis vi f. eks. skal opdatere kundens navn mere end 1 sted, opstår der risiko for, at vi laver fejl det ene sted. Så kan vi ikke stole på vores data.

Redundans

Redundans betyder “overflødig”, og hvis vi ikke passer på, så vil vores database rumme en masse overflødige data.

Hvis vi tager en kunde som eksempel. Vedkommendes navn, adresse og postnummer er helt tilfældige. Men “By” afhænger 100% af “Postnummer”. Vi kan ikke skrive “6000 Fredericia” eller “7000 Esbjerg”. Byens navn er redundant, så det anbringer vi i en tabel for sig med postnummer som primærnøgle i den nye tabel og samtidig fremmednøgle i den gamle tabel.

Referentiel integritet

Frygteligt ord, ikke? “Referentiel integritet”. Men det er ikke så svært. Se ovenfor. Alle postnumre, som vi bruger som fremmednøgle, SKAL svare til en primærnøgle. Hvis vi sætter postnummer 5000 ind som fremmednøgle under Kunder, så skal den også findes under tabellen “Postnummer_by”. Ellers virker vores database ikke!

Normalformer

Normalisering består af 3 trin, men vi vil kun arbejde med 2 trin her. Grunden er, at trin 2 og 3 ikke er til at skelne fra hinanden.

  • 0. Normalform
    • Svarer bare til, at vi skriver en usorteret iste ned over entiteter og attributter. Du kan sige, at det kunne være resultatet af en brainstorm over, hvad vi overhovedet vil have med
  • 1. Normalform
    • 1.1 Alle entiteter skal have en primærnøgle
    • 1.2 “Atomisering”, dvs. “opdeling”. Data skal deles op, hvis man vil sortere. “Navn” kan f.eks. med fordel deles op i “Fornavn” og “Efternavn”
  • 2. + 3. Normalform
    • Hvis nogle data afhænger 100% af andet end primærnøglen, så skal de splittes op i en ny tabel. Eksempel er “postnummer” og “by”. By afhænger af postnummer, så derfor skal de ud i en selvstændig tabe
    • Husk at forbinde de 2 tabeller lige som vi gjorde under “relationer”

Se evt. afsnit 20.3 Normalisering for at repetere normalisering.

Link til toppen.

23.7 Normalisering af eksemplet med bilejer og bil

Start med E-R diagrammet

Et godt sted at starte med normalisering er E-R diagrammet. Vi nåede hertil i afsnit 23. E-R diagrammer også med attributter.

E-R diagram

Vi er allerede godt i gang med normaliseringen i E-R diagrammet. Dels fordi vi har en opdeling i entiteter og attibutter, dels fordi vi har relationer mellem entiteterne. Vi skal dog køre processen med normalisering igennem alligevel.

1. Normalform

Første normalform kræver 2 ting. At alle entiteter har et ID og at data er tilstrækkeligt splittet op.

1. Normalform, alle entiteter skal have en primærnøgle

Først skal alle entiteter have et ID:

I E-R diagrammet er der ingen af tabellerne, der har en primærnøgle, så det skal vi gøre noget ved. Derfor skal vi se, om vi kan finde en naturlig primærnøgle blandt den enkelte entitets attributter.

Først “Bilejer”. Vi kan bruge “Kørekort nummer” som ID, da alle kørekort ID er unikke. Men kørekort bliver skiftet ud. Det er sikrere enten at tilføje en ny attribut, som vi så kan kalde “Bilejer-id”. Altnernativt: Hvis det er politiets database, så kunne vi bruge borgernes CPR nummer.

Så “Bil”. Her har vi en naturlig primærnøgle: “Nummerplade”. Vi har sådan set også bilens stelnummer, men nummerplade er noget nemmere at aflæse :-). “Bilejer-id” og “Nummerplade” bliver primærnøgler i de 2 tabeller.

Da 1 bilejer kan eje mange biler, skal vi skabe en forbindelse mellem de 2 entiteter. Det gør vi ved at lave en ny attribut under Biler. Her skal Bilejer-id stå.

Ændringerne i E-R diagrammet viser jeg nedenfor. For overskuelighedens skyld viser jeg kun primær- og fremmednøglerne;

1. Normalform ,”atomisering” eller opsplitning

Hvis vi ønsker at sortere på oplysninger i et felt, så skal vi lave en “atomisering” eller opsplitning.

Se nedenfor: hvor oplysningerne virkeligt er blandet sammen. Hvis vi ønsker at sortere på fornavn og efternavn, så skal vi splitte op i 2 selvstændige kolonner. “Adresse” kunne også være fin at splitte op i “Adresse” “Postnummer” og “By”. Til gengæld giver det ikke mening at dele adressen i vej og husnummer. Vi har ikke behov for at sortere vej og husnummer særskilt.

Tip: Sæt værdier ind i attributterne

Det kan være svært at gennemskue, om en attribut skal deles op eller ej. Hvis du sætter testværdier ind, som jeg har gjort ovenfor, så er det noget nemmere at gennemskue. Jeg anbefaler kraftigt, at du indsætter testværdier i tabellerne.

Start databasen
Start databasen

I eksemplet bilejer – bil er det nødvendigt at splitte op i fornavn – efternavn. Vi splitter også Postnummer-by op i 2, en attribut til Postnummer og en til By.

Efter 1. normalform ser E-R diagrammet nu sådan ud. De nye attributter er med lyserød:

Normalisering 1. NF

Tip til E-R diagrammet

Når du laver dit E-R diagram med attributter, så sørg for, at alle entiteter har en primærnøgle. Sørg også for, at tabeller, der refererer til andre tabeller, har en fremmednøgle.

Inden vi går videre: Skift fra E-R diagrammer til tabeller

Vores E-R diagram er blevet lidt uoverskueligt. Derfor skifter jeg til tabeller. Jeg laver tabellerne i Excel. De er meget nemmere at overskue. “PK” står for “primary key” eller primærnøgle.

2. + 3. normalform

Vi skal nu undersøge, om der findes attributter, som IKKE afhænger af primærnøglen.

I entiteten Bilejer har vi det klassiske eksempel: By afhænger af Postnummer. By er redundant, så den skal flyttes over i en ny tabel. Se evt. afsnit 23.6 Begreb er inden for normalisering under Redundans. Der bliver en 1-til-mange relation mellem Postnummer_by, da:

  • ET postnummer kan forekomme hos MANGE kunder
  • EN kunde kan have ET postnummer

I entiteten “Bil” er der ingen redundante data, så det er ikke nødvendigt at splitte op her.

Resultatet bliver som vist nedenunder Bemærk, at jeg har indsat primærnøglen fra Bilejer som fremmednøgle under Bil. Postnummer under Bilejer bliver fremmednøgle til den nye entitet Postnummer_by. Postnummer bliver selvfølgelig primærnøgle i den nye tabel. Så endelig er vi i mål. For at lette forståelsen har jeg vist 1-til-mange relationerne .

Link til toppen.

23.8 Opsummering

Her er en kort opsummering af, hvordan du kommer fra en ide om en database og til en færdig, relationel database.

PUNKTNOTE
1. BrainstormLav en liste over alt, hvad du gernevil have i din database. Du får en usorteret liste, hvor entiteter og attributter står hulter til bulter. Se afsnit 23.1 Start med en barinstorm over ønsker til databasem
2. Opdel i entiteter og attributterArbejd med din liste, så du får sorteret i entiteter og attributter, og hvor attributterne er underpunkter til entiteterne. Tilføj gerne flere entiteter og attributter om nødvendigt. Se afsnit 23.2 Entiteter, relationer og attributter

Liste
3. Lav et E-R diagramTegn et E-R diagram og bestem relationen mellem de forskellige entiteter. Se afsnit 23.3 Afbilding af entiteter og relationer i et E-R diagram samt de efterfølgende 2 afsnit.
4. NormaliserTil sidst skal du normalisere de enkelte entiteter. Dine entiteter bliver til tabeller, dine attributter bliver til kolonner i de enkelte tabeller. Tabellerne er forbundet til hinanden med relationer gennem primær- og fremmednøgler. Se afsnit 27. Normalisering af eksemplet med bilejer og bil
Færdig database

Videoen giver dig også en opsummering:

Næste trin er at få databasen ind i en rigtigt RDBMS-program, så vi kan bruge den i en app/et program. Det gør vi i næste kapitel.

Link til toppen.

Kapitel 24 Sæt det hele sammen med MySQL databasen

Vi er nu kommet til det sidste trin i en lang kæde, der startede helt tilbage med billedbehandling i kapitel 2 og HTML kodning i kapitel 5. Kan du huske oversigten her

Kernen i figuren er 3-lags opdelingen med Frontend, Backend og Database.

Til frontend anvender alle sprogene HTML, CSS og JavaScript. De er de eneste sprog, som kan læses i en browser uden at være installeret på en server

Til backend har vi en noget større udvalg. Typiske valg er Microsofts meget populære C#, men open-source PHP er også populært. I princippet kan man bruge hvad som helst, bare sproget bliver installeret på en server. Hvis serveren kan afvikle sproget, så er alt godt. Jeg valgte at bruge PHP, da det er open-source og sproget bag WordPress.

Til database-delen har vi også flere muligheder. Vi skal under alle omstændigheder bruge sproget SQL for at kommunikere fra backend til databasen. I hvert fald hvis vi arbejder med relationelle databaser, som er den type databaser vi arbejder med her. I afsnit 22.6 RDBMS = Databaseprogrammer så du en oversigt over de mest almindelige databaser.

Alle relationelle databaser har det til fælles, at de lige som backend-sprog skal installeres på en server. PHP er et open-source sprog, og PHP udviklere vælger ofte databasen MySQL, som også er open-source. MySQL er den database, jeg vil bruge i kapitlet her. WordPress bruger f.eks. MySQL.

Link til toppen.

24.1 Et par underholdende fakta om MySQL databasen

Brugerfladen på MySQL virker primitiv og MySQL er er open-source. Men tag ikke fejl, MySQL bliver brugt i meget stor skala, og den er på top-3 listen over verdens mest populære databaseprogrammer sammen med betalingsdatabaser som Oracle og Microsoft SQL Database (ikke at forveksle med Microsoft Access). Den er enkel men meget effektiv og driftssikker. Den kører på alle platforme dvs. Mac, Windows og Linux, og den bliver brugt til enorme mængder af data. Eksempler på store organisationer, der bruger MySQL databasen:

  • Twitter, Facebook, Wikipedia
  • WordPress bruger PHP og MySQL. 14,7% af verdens 100 mest besøgte hjemmesider er lavet i WordPress, selvom WordPress primært retter sig mod blogs og mindre hjemmsider
  • Walt Disney, Sony og BBC bruger WordPress
  • I omegnen af 75 mio hjemmesider bruger WordPress, vel 30% af alle hjemmesider
  • Ca 30% af alle webshops bruger WordPress
  • Tallene er fra 2019, kilde https://techjury.net/blog/percentage-of-wordpress-websites/#gref

Her ser du administrationsmodulet til MySQL databasen, Det hedder “phpMyAdmin”.

Link til toppen.

24.2 Kort om kapitlet

Jeg vil vise et gennemgående eksempel, hvor jeg opretter en lille kundedatabase.

  • Vi skal oprette en database med data i MySQL databasen
  • Vi skal skabe en forbindelse til databasen fra PHP, dvs. backend
  • Vi skal hente data med SQL
  • Vi skal præsentere data, f.eks. som en tabel, i frontend

Med andre ord: Når du er igennem det her, har du hele pakken: Fra frontend over backend til database og tilbage igen.

Kundedatabasen ser sådan her ud som E-R diagram:

Jeg prøver at vise den generelle tilgang her i kapitlet. Mere specifikt arbejder jeg i opgaverne med Visual Studio Code, og du kan finde den for Visual Studio Code specifikke tilgang i Vejledning 7. PHP og MySQL i Visual Studio Code.

Ud over det er der som sædvanlig er der god hjælp at hente hos W3schools. De har et område under deres PHP vejledninger, der kun handler om MySQL.

Du kan vælge at oprette data i MySQL databasen på flere måder. Jeg vil oprette en lille kundedatabase.

Link til toppen.

24.3 Opret database med tabeller i PhpMyAdmin (MySQL)

Der er et gratis administrationssystem til MySQL, som hedder PhpMyAdmin. Du kan oprette data i MySQL databasen på flere måder. Jeg vil vise dig, hvordan du kan oprette data gennem SQL kode samt gennem import af data.

  • (1). Gå ind under faneblandet Databaser
  • (2). Giv først databasen et navn. Vælg så dansk charsæt og klik til sidst på knappen Opret

Nu skulle du gerne kunne se din database ude til venstre (1). Bemærk, at den står med småt. PhpMyAdmin inviterer allerede nu til at oprette en tabel, som du kan se med beskeden Opret tabel (2). Sjovt nok er “Opret tabel” ikke en knap.

Start med at give tabellen et navn (1). Vælg antal kolonner (2) og her skal du være skarp. Det kan være svært at tilføje kolonner efterfølgende. I E-R diagrammet er der 7 attributter på entiteten kunde, så det skal der også være her. Skriv derfor 7 i Antal kolonner. Klik til sidst (3) på knappen Udfør.

Nu skal vi oprette de enkelte kolonner i tabellen. Først en oversigt over vinduet, som du skal udfylde. Indtast kolonnenavn, datatype og evt. længde til venstre (1). Vær opmærksom på kolonnerne kunde-id (2), telefonnummer og email (3). Se også tabellen herunder, hvor du får lidt mere hjælp.

Følg tabellen her, når du udfylder:

RÆKKEUDFYLDES SÅDAN
kunde-idDatatype: INT. Længde er ikke nødvendig. Under Indeks vælg PRIMARY, da det er primærnøglen. Lige efter Indeks sæt flueben under A_I, som står for “Auto increment”. Så bliver alle nye værdier sat til højeste værdi + 1
fornavnDatatype: VARCHAR. Længde 50
efternavnDatatype: VARCHAR. Længde 50
adresseDatatype: VARCHAR. Længde 50
postnummerDatatype: INT. Længde er ikke nødvendig.
telefonnummerDatatype: VARCHAR. Længde 50. Sæt flueben ud for Nulværdi (3), da vi tillader, at feltet ikke udfyldes
emailDatatype: VARCHAR. Længde 50. Sæt flueben ud for Nulværdi (3), da vi tillader, at feltet ikke udfyldes

Når du er færdig med at udfylde. Skrol ned i bunden af det store vindue og klik på knappen Gem (1) for at gemme dit arbejde. Bemærk at du kan se dit arbejde som SQL kode ved at klikke på knappen Forhåndsvis SQL (2). Det viser jeg lidt mere om efter vinduet her.

Hvis du klikker på knappen Forhåndsvis SQL, så ser du SQL koden til at oprette tabellen. Den ser sådan ud. Det kan være nyttigt at have SQL koden, som jeg har indsat nedenfor. Så kan vi hurtigt oprette en ny tabel, hvis noget skulle gå galt:

CREATE TABLE `kundedatabase`.`kunde` 
( `kunde-id` INT NOT NULL AUTO_INCREMENT , `fornavn` VARCHAR(50) NOT NULL , `efternavn` VARCHAR(50) NOT NULL , `adresse` VARCHAR(50) NOT NULL , `postnummer` INT(50) NOT NULL , `telefonnummer` VARCHAR(50) NULL , `email` VARCHAR(50) NULL , PRIMARY KEY (`kunde-id`)) 
ENGINE = InnoDB;

Når du har gemt, skifter PhpMyAdmin nu skærmbillede til fanen Struktur (1), så du kan se, hvad du har lavet. Du kan se din kolonne både som en række med attriutter (2), og du kan se den til venstre (3) ude under oversigten over databaser.

Nu mangler vi en tabel, “postnummer_by”. Inde i PhpMyAdmin gå ind yderst til venstre (1) og klik på linket Ny. Øverst under Tabelnavn(2) giv tabellen navnet postnummer_by. Lav 2 kolonner (3). Den ene skal hedde postnummer og have datatypen INT. Den anden skal hedde by og have datatypen VARCHAR med en længde på 50. Husk under Indeks (4) at sætte postnummer til PRIMARY, så den bliver til primærnøglen. Det er ikke nødvendigt at sætte flueben i feletet A..I.

Her er SQL koden.

CREATE TABLE `kundedatabase`.`postnummer_by` 
( `postnummer` INT NOT NULL , `by` INT(50) NOT NULL , PRIMARY KEY (`postnummer`)) 
ENGINE = InnoDB;

Det færdige resultat ser sådan ud. Du ser både den nye tabel ude til venstre (1) og i selve hovedvinduet (2).

Du har nu oprettet de 2 tabeller, som der skal til. Når du får mere erfaring, vil du kunne gøre det her langt hurtigere :-), evt. kun ved hjælp af kode.

Link til toppen.

24.4 Indsæt data i din første tabel

Du kan skrive data direkte ind i tabellerne i PhpMyAdmin. Find den tabel, du vil arbejde med, ude til venstre (1). Gå ind under fanen Indsæt (2). Skriv værdierne i hovedområdet midt i (3). Du skal ikke udfylde noget for feltet kunde-id, den er sat til auto-increment da du oprettede den, så den sætter selv et nyt og unikt tal ind for hver række.

Når du har udfyldt, klik på knappen Udfør, som du finder ved at skrolle lidt ned i hovedvinduet.

Når du har indsat, så skifter PhpMyAdmin til et vindue, hvor du kan se SQL koden. Koden ser du nedenfor:

INSERT INTO `kunde` 
(`kunde-id`, `fornavn`, `efternavn`, `adresse`, `postnummer`, `telefonnummer`, `email`) 
VALUES (NULL, 'Kim', 'Kunde', 'Rabatvej 3', '6000', '12312312', 'kim@kunde.dk');

Selve vinduet ser sådan her ud. Klik på knappen SELECT * (1), for vi vil tjekke, at alle data er inde i tabellen. Ret koden til, så der kun står “SELECT * FROM kunde” (2). Kør forespørgslen ved at klikke på knappen Udfør (3).

Du kan se resultatet af forespørgslen i hovedvinduet (1), og du kan også se, at tabellen nu har et indhold:

Indsætte flere værdier i tabellen “kunde”

Du ved nu, hvordan du indsætter værdier i en tabel. Du skal nu indsætte flere værdier. Der er 2 muligheder: Du kan indsætte dem ved at finde på nogle kunder og bruge vinduet Indsæt præcis som før. Du kan også køre scriptet her under fanen SQL. Scriptet indsætter nogle kunder i tabellen. I starten vil du nok foretrække at skrive det ind manuelt, men efterhånden vil det være hurtigere for dig at sætte data ind vha. SQL end vha. indtastning.

INSERT INTO `kunde` 
(`kunde-id`, `fornavn`, `efternavn`, `adresse`, `postnummer`, `telefonnummer`, `email`) 
VALUES (NULL, 'Rikke', 'Rabat', 'Købegade 4', '7000', '23232323', 'rikke@kabat.dk');
INSERT INTO `kunde` 
(`kunde-id`, `fornavn`, `efternavn`, `adresse`, `postnummer`, `telefonnummer`, `email`) 
VALUES (NULL, 'Kamilla', 'Krejler', 'Handelsvej 5', '6000', '24422442', 'kamilla@krejler.dk');
INSERT INTO `kunde` 
(`kunde-id`, `fornavn`, `efternavn`, `adresse`, `postnummer`, `telefonnummer`, `email`) 
VALUES (NULL, 'Per', 'Procent', 'Pengegade 1', '6000', '45544554', 'per@procent.dk');
INSERT INTO `kunde` 
(`kunde-id`, `fornavn`, `efternavn`, `adresse`, `postnummer`, `telefonnummer`, `email`) 
VALUES (NULL, 'Signe', 'Shopping', 'Posevej 4', '6000', '32233333', 'signe@shopping.dk');

Her er scriptet indsat (1). Husk at gøre det under fanen SQL (2). Klik til sidst på knappen Udfør (3).

Som altid når du har ændret på indholdet i dine tabeller, så kør scriptet her “SELECT * FROM kunde” for at tjekke, om data nu også er inde i tabellen. Her er resultatet af min SELECT * FROM kunde – forespørgsel.

Link til toppen.

24.5 Importer data fra en fil

Kender du .csv filer? En .csv fil er en slags fladgjort Excel-fil. Den er meget primitiv, den består kun af data og noget til at adskilde data og rækker med, typisk komma eller semikolon.

Fordi formatet er så primitivt, så er formatet super velegnet til eksport og import af data. Jeg har lavet en .csv fil til dig her over postnumre i Danmark. Jeg vil bruge den til at importere data til tabellen postnummer_by. Der er over 1.200 forskellige postnumre i Danmark, så du sparer noget tid ved at importer data i stedet :-).

I praksis vil du ofte importere data frem for at indtaste. Her er filen. Download den, hvis du vil prøve vejledningen af! Bemærk dog, at du skal være meget præcis. Antallet af kolonner i tabellen skal svare til antallet af kolonner i .csv filen.

Start med at finde den rigtige tabel, postnummer_by (1). Vælg fanen Importer (2) og vælg filen postnumre.csv (3). Du kan som sagt downloade den på knappen lige over teksen her.

Skrol længere ned i vinduet. Under Format (4) vælg CSV og under Kolonner adskildt med skal du ændre tegnet, så der står et semikolon (dvs. “;”). Klik til sidst på knappen Udfør nederst (ikke vist på figuren nedenfor).

Når jeg indsætter, så kommer der en fejl. Men det virker alligevel. Under fanebladet SQL test efter ved at køre scriptet “SELECT * FROM postnummer_by”. Det ser fint ud hos mig (1), og øverst (2) står der, at jeg har importeret ikke mindre end 1126 rækker i alt.

Link til toppen.

24.6 Skab forbindelse mellem databasen og backend

Først ændringer i PhpMyAdmin …

Jeg vil bruge brugerfladen i VIsual Studio Code (fremover VS-Code) til at opbytte min kode i. Jeg forudsætter, at modulet SQLTools er installeret. Hvis du er i tvivl, så kik i notatet Vejledning 7. PHP og MySQL i Visual Studio Code helt nede under punkt 5.

Allerførst skal vi have styr på brugernavn og password. VS-Code vil bede os om brugernavn og password, så det skal vi have sat op. I PhpMyAdmin klik på fanebladet Privilegier (1). Jeg har en bruger, der hedder “Lars”. Klik bagefter på Handling (2) i den øverste linje, dvs. den øverste bruger.

Nu er du inde i et nyt vindue. Gå ind under fanebladet (1) Skift adgangskode. Under Adgangskode (2) skriv et password. Normalt helst et svært password, men da jeg kun skal bruge databasen lokalt, så bruger jeg bare passwordet “Db”. Husk at gemme dine ændringer ved at klikke på knappen Udfør. Hvis du vil ændre brugernavn, så gå ind under fanebladet (3) Login-information.

… så til selve VS-Code

Gå ind i VS-Code under SQLTools (1) og klik på knappen (2) Add new connection.

I det næste skærmbillede vælg MySQL (1).

Giv din forbindelse et navn, jeg har kaldt min for “connect” (1). Under (2) udfylder du således:

  • Database*. Du skal bruge navnet på den specifikke database, her “Kundedatabase”
  • Username*. Har du fra PhpMyAdmin. Min bruger hedder “Lars”
  • Use password. Vælg “Save password”
  • Password* (kun synlig hvis du har valgt “Save password”). Jeg har “Db” som password

Klik til sidst på knappen Test connection (3).

Når den er færdig, har du forhåbentlig en grøn succesoplevelse (1). Hvis det er tilfældet, så skynd dig at klikke på knappen Save connection (2).

V

Hvis det virker, så får du nedenstående skærmbillede. Klik på knappen (1) CONNECT NOW.

VS-Code åbner et faneblad (1) connect.session.sql. Til venstre (2) kan du se tabellerne i databasen. Skriv din SQL kode (3), her “SELECT * FROM kunde”,. Hvis du klikker på linket Run on active connection. Du finder linket stadig i punkt 3 lige oven over den SQL koden. Det står med ret små bogstaver. Til højre (4) kan du se resultatet. Det virker, der er forbindelse!

Link til toppen.

24.7 Hent og vis data med PHP

Det virker måske lidt klodset på dig men: Vi har kun SQL i forhold til at hente data ud af en database. Den måde, vi så gør det på, er at pakke SQL koden ind i et backend sprog og arbejde med den dér. Jeg viser

Koden nedenfor har jeg som sædvanlig hentet hos W3 schools. Bemærk: Du finder intet hjælp til MySQL databasen under (1) SQL. Gå i stedet ind under (2) PHP og skrol langt ned i venstremenuen. På et tidspunkt kommer du til et punkt (3) MySQL Database, og her er alt, hvad du har brug for. Jeg havde her brug for (4) MySQL Select Data. Link: https://www.w3schools.com/php/php_mysql_select.asp

Resultatet ser sådan ud: Ikke noget særligt hvad udseende angår, men vi kan altid gøre det pænere med CSS og HTML.

Reducer dine fejlkilder: Test SQL koden separat

Det er nemt at lave fejl her, for koden er kompleks. Jeg anbefaler meget kraftigt, at du altid tester dit SQL kald af enten i VS-Code eller i PhpMyAdmin inden, at du indsætter det i koden. Så er du sikker på, at i hvert fald din SQL kode virker

Lad os kikke på koden bag. Den er med vilje gjort så enkel som muligt. Selve koden, som er lige til at kopiere, har du her. Bagefter kikker vi på den i detaljer.

<!DOCTYPE html>
<html>
<head>
    <title>Vis data fra en mysql database</title>
</head>
<body>
<h1>Vis data</h1>
<?php
// opret forbindelse til serveren
$servername = "localhost";
$username = "Lars";
$password = "Db";
$dbname = "kundedatabase";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// lav SQL kaldet
$sql = "SELECT 'kunde-id', fornavn, efternavn FROM kunde";
// hent resultatet ind i en variabel
$result = $conn->query($sql);

// tjek om der overhovedet er et antal rækker
if ($result->num_rows > 0) {
    // Hvis der er et antal rækker, så udskriv dem
    while($row = $result->fetch_assoc()) {
        // bemærk, at vi blander html og php efter forgodtbefindende
        echo "<br> Kundeid: ". $row["kunde-id"]. " - navn: ". $row["fornavn"]. " " . $row["efternavn"] . "<br>";
    }
} else {
    echo "0 results";
}

// luk forbindelsen igen, ellers åbner du en motorvej for hackning
$conn->close();

?>
</body>
</html>

Koden i detaljer

KODELINJE FORKLARING
1- 7Helt almindelig HTML som du kender i forvejen
9 – 13Jeg opretter en række variable. De er nødvendige for at kunne forbinde til databasen. Variablene kan have andre navne hos dig, men du har grundlæggende brug for de 4 stk: $servername, $username, $password og $dbname
15Her laver vi forbindelsen til databasen
18 – 19Hvis kaldet til databasen fejler, så giv brugeren en fejlmeddelelse
23Skriv SQL kaldet (som så bliver pakket ind i PHP kode)
25Hent resultatet af SQL kaldet ind i en variabel
28Tjekker om variablen med resultatet af SQL kaldet er tom
30 – 32 og 35Udskriver resultaterne vha. et while-loop. Hvis der ikke er nogle, så får brugeren besked i linje 35
39Husk at lukke forbindelsen til databasen igen, ellers efterlader du en motorvej til hackere

Er det sådan, at de “rigtige” udviklere gør?

Både ja og nej. Kernen er som sagt at pakke SQL koden ind i backend kode, og det ændrer sig ikke selv for professionelle udviklere. Der findes dog mere elegante metoder end dem, jeg beskriver her, f.eks. kan man lige som i CSS genbruge en del af koden. Men: Forstår du det her, så er du meget langt i kodning 🙂

Link til toppen.

24.8 Gør siden lidt pænere

Jeg vil lave øvelsen fra sidste opgave igen. Men denne gang vil jeg hente alle værdier, dvs. værdier fra begge tabeller, og præsentere dem pænt i en html tabel. Resultatet kommer til at se sådan her ud:

Overordnet set blander noget HTML ind i min PHP kode. Repeter evt. hos W3 Schools hvordan man opbygger en tabel, for det er det, jeg gør: https://www.w3schools.com/html/html_tables.asp.

Øverst (1) har jeg tabelhovedet. I et WHILE-loop (2) laver jeg så rækkerne i tabellerne. Så længe, at der er rækker i variablen $result (som jo er resultatet af min SQL forespørgsel i databasen), så vil WHILE løkken blive ved med at danne tabelrækker. Til sidst (3) skal jeg selvfølgelig afslutte min tabel med </table>.

Her er koden til min .php fil:

<!DOCTYPE html>
<html>
<head>
    <title>Vis data fra en mysql database på en pæn måde</title>
</head>
<body>
<h1>Vis data</h1>
<p>Her viser jeg alle data fra tabellerne, og jeg viser dem i en tabel.</p>
<?php
// opret forbindelse til serveren
$servername = "localhost";
$username = "Lars";
$password = "Db";
$dbname = "kundedatabase";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// lav SQL kaldet
$sql = "SELECT kunde.kunde_id, kunde.fornavn, kunde.efternavn, kunde.adresse, kunde.postnummer, postnummer_by.by, kunde.telefonnummer, kunde.email FROM kunde INNER JOIN postnummer_by ON postnummer_by.postnummer=kunde.postnummer";

//SELECT kunde.kunde-id, kunde.fornavn, kunde.efternavn, kunde.adresse, kunde.postnummer, postnummer_by.by, kunde.telefonnummer, kunde.email FROM kunde INNER JOIN postnummer_by ON postnummer_by.postnummer=kunde.postnummer";
// hent resultatet ind i en variabel
$result = $conn->query($sql);

// tjek om der overhovedet er et antal rækker
if ($result->num_rows > 0) {
    // Hvis der er et antal rækker, så udskriv dem pænt i en tabel. Start med tabelhovedet
    echo "<table>
    <tr>
    <th>Kundeid</th>
    <th>Fornavn</th>
    <th>Efternavn</th>
    <th>Adresse</th>
    <th>Postnummer</th>
    <th>By</th>
    <th>Telefonnummer</th>
    <th>Email</th>  
    </tr>";
    while($row = $result->fetch_assoc()) {
        // bemærk, at vi blander html og php efter forgodtbefindende
        echo "<tr>";
        echo "<td>". $row["kunde_id"] . "</td>";
        echo "<td>". $row["fornavn"] . "</td>";
        echo "<td>". $row["efternavn"] . "</td>";
        echo "<td>". $row["adresse"]."</td>";
        echo "<td>". $row["postnummer"]. "</td>";
        echo "<td>". $row["by"]. "</td>";
        echo "<td>". $row["telefonnummer"]. "</td>";
        echo "<td>". $row["email"]. "</td>";
        echo "</tr>";
    }
    echo "</table>";
} else {
    echo "0 results";
}

// luk forbindelsen igen, ellers åbner du en motorvej for hackning
$conn->close();

?>
</body>
</html>

Link til toppen.

24.9 Indsæt data

At indsæt data er lidt mere kompliceret end at hente data. Selve kontakten til databasen er det samme, men brugeren er nødt til at kunne indtaste data i frontend. Bagefter skal backend kunne samle data op og sætte dem ind i databasen. W3 schools skriver om at indsætte data med PHP på https://www.w3schools.com/php/php_mysql_insert.asp.

Start med at teste et SQL kald af i PhpMyAdmin. Det har jeg gjort, og kaldet her virker. Jeg har bagefter slettet den igen.

INSERT INTO kunde (fornavn, efternavn, adresse, postnummer, telefonnummer, email) 
VALUES ('Kenneth', 'Kvittering', 'Saldovej 2', 5000, '98769876', 'kenneth@kvittering.dk')

Backend koden ser sådan her ud, når det kun gælder om at indsætte. Bemærk, at det absolut eneste nye er, at jeg (1) i linje 22 og 23 har skiftet SQL koden og at jeg i linje 25 – 29 giver brugeren en lidt anden besked, hvis det går godt hhv. skidt med at afvikle koden. Alt andet er præcis det samme som før. Dvs. at oprette forbindelse til serveren, skabe og tjekke forbindelsen, sende kaldet af sted og ikke mindst at lukke forbindelsen er præcis det samme!

Men det her er ikke godt nok, for vi kan ikke forvente, at en bruger kan kode SQL kode. Faktisk ville det være lodret uforsvarligt, hvis vi lod brugerne få direkte adgang til backend. Så vi skal skabe en brugerflade.

Jeg har her lavet en primitiv, men effektiv brugerflade, men uden forbindelse til databasen. Resultatet ser sådan ud. Test det gerne af. Først indtaster du data (1) i formularen, så klikker du (2) på knappen Send, og så skulle du gerne se data nedenunder (3). Hvis ja, så virker formularen.

Jeg har lavet et testdokument, så du kan repetere, hvordan man laver en formular og henter data fra formularen over i backend. Du kan kopiere koden nedenfor, men først en bemærkning.

For at hindre, at alting sker før, at jeg har klikket på knappen, har jeg pakket noget af koden ind i en funktion. Funktionen hedder “udskriv_data”, og den starter i linje 51 (3).

Når brugeren klikker på knappen Send, så tager koden fat fra linje 38 (1). Når koden så når til linje 48, så bliver funktionen “udskriv data” kaldt (2) med variable. I linje 51 (3) starter funktionen så, og den starter med at modtage alle de variable, som er sendt fra linje 48. Det betyder, at indholdet af funktionen først går i gang, når den bliver kaldt.

Bemærk: Når man kalder en funktion og sender data med over, siger man normalt, at man kalder med “parametre”, ikke “variable”.

Du kan kopiere koden nedenfor direkte ind i et .php dokument og teste løs.

<!DOCTYPE html>
<html>
<head>
    <title>Opret data i SQL database</title>
</head>
<body>
<h1>Opret data</h1>
<form method="post" action="">  
<table>
   <tr>
     <td>Fornavn: </td>
     <td><input type="text" name="fornavn"></td>
   </tr>
   <tr>
     <td>Efternavn: </td>
     <td><input type="text" name="efternavn"></td>
   </tr>
   <tr>
     <td>Adresse:</td>
     <td><input type="text" name="adresse"></td>
   </tr>
   <tr>
     <td>Postnummer: </td>
     <td><input type="text" name="postnummer"></td>
   </tr>
   <tr>
    <td>Telefonnummer:</td>
    <td><input type="text" name="telefonnummer">
   </tr>
   <tr>
    <td>Email: </td><td><input type="text" name="email"></td>
   </tr>
  </table>
  <input type="submit" name="submit" value="Send">  
</form>
<?php
// hent alle variable fra formularen
if ($_SERVER["REQUEST_METHOD"] == "POST") {
  // opret og initialiser variablene
  $fornavn = $efternavn = $adresse = $postnummer = $telefonnummer = $email = 0;
  // hent data fra formularen
  $fornavn = $_POST["fornavn"];
  $efternavn = $_POST["efternavn"];
  $adresse = $_POST["adresse"];
  $postnummer = $_POST["postnummer"];
  $telefonnummer = $_POST["telefonnummer"];
  $email = $_POST["email"];
  udskriv_data($fornavn, $efternavn, $adresse, $postnummer, $telefonnummer, $email);    
}

function udskriv_data($fornavn, $efternavn, $adresse, $postnummer, $telefonnummer, $email){
    // test på, om jeg henter input rigtigt
    echo "<h2>Du har indtastet ...</h2>";
    echo $fornavn;
    echo "<br>";
    echo $efternavn;
    echo "<br>";
    echo $adresse;
    echo "<br>";
    echo $postnummer;
    echo "<br>";
    echo $telefonnummer;
    echo "<br>";
    echo $email;
    }
?>
</body>
</html>

Separation of concerns

Jeg blander koden ret meget her. Frontend, dataopsamling, kommunikation med databasen, alt muligt. Jeg gør det for at holde det så simpelt som muligt. I praksis kunne man gøre en del for at skille koden ad i forskellige dele. Faktisk er jeg startet på det, når jeg deler min PHP kode op i funktioner

Nu er vi endelig fremme ved målstregen. Når du først har fået SQL kaldet til at virke, du har testet det af, og du har styr på formularen, så er det ret nemt. Nedenfor ser du den komplette kode, men først et par bemærkninger.

  • Øverst (1) henter jeg det, som brugeren indtaster, ind som variable.
  • Længere nede (2) kalder jeg funktionen insert_data med alle de ting, som brugeren har indtastet
  • Ved (3) modtager funktionen indsert_data alle de data, den har brug for.

Alt det her har du set før. Det nye er (4), at jeg fletter variablene ind i mit SQL kald. Det viser jeg i stor forstørrelse nedenfor.

Øverst i tabellen ser du den almindelige SQL kode. Nederst sammenfletningen med variablene. INSERT INTO er helt den samme, VALUES er anderledes. Smart, ikke. Jeg skriver bare variablene ind i stedet for de værdier, jeg normalt ville indtaste.

KODETYPESELVE KODEN
Alm. SQLINSERT INTO kunde (fornavn, efternavn, adresse, postnummer, telefonnummer, email)
VALUES (‘Kenneth’, ‘Kvittering’, ‘Saldovej 2’, 5000, ‘98769876’, ‘kenneth@kvittering.dk’)
SQL flettet med PHPINSERT INTO kunde (fornavn, efternavn, adresse, postnummer, telefonnummer, email) 
VALUES (‘$fornavn’, ‘$efternavn’, ‘$adresse’, $postnummer, ‘$telefonnummer’, ‘$email’)

Til sidst hele koden, som du bare kan kopiere ind i et .php dokument.

<!DOCTYPE html>
<html>
<head>
    <title>Opret data i SQL database</title>
</head>
<body>
<h1>Opret data</h1>
<form method="post" action="">  
<table>
   <tr>
     <td>Fornavn: </td>
     <td><input type="text" name="fornavn"></td>
   </tr>
   <tr>
     <td>Efternavn: </td>
     <td><input type="text" name="efternavn"></td>
   </tr>
   <tr>
     <td>Adresse:</td>
     <td><input type="text" name="adresse"></td>
   </tr>
   <tr>
     <td>Postnummer: </td>
     <td><input type="text" name="postnummer"></td>
   </tr>
   <tr>
    <td>Telefonnummer:</td>
    <td><input type="text" name="telefonnummer">
   </tr>
   <tr>
    <td>Email: </td><td><input type="text" name="email"></td>
   </tr>
  </table>
  <input type="submit" name="submit" value="Send">  
</form>
<?php
// hent alle variable fra formularen
if ($_SERVER["REQUEST_METHOD"] == "POST") {
  // opret og initialiser variablene
  $fornavn = $efternavn = $adresse = $postnummer = $telefonnummer = $email = 0;
  // hent data fra formularen
  $fornavn = $_POST["fornavn"];
  $efternavn = $_POST["efternavn"];
  $adresse = $_POST["adresse"];
  $postnummer = $_POST["postnummer"];
  $telefonnummer = $_POST["telefonnummer"];
  $email = $_POST["email"];

  insert_data ($fornavn, $efternavn, $adresse, $postnummer, $telefonnummer, $email);
}

function insert_data($fornavn, $efternavn, $adresse, $postnummer, $telefonnummer, $email){
    $servername = "localhost";
    $username = "Lars";
    $password = "Db";
    $dbname = "kundedatabase";
    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    // Check connection
    if ($conn->connect_error) {
      die("Connection failed: " . $conn->connect_error);
    }

    $sql = "INSERT INTO kunde (fornavn, efternavn, adresse, postnummer, telefonnummer, email) 
    VALUES ('$fornavn', '$efternavn', '$adresse', $postnummer, '$telefonnummer', '$email')";
    
    if ($conn->query($sql) === TRUE) {
      echo "New record created successfully";
    } else {
      echo "Error: " . $sql . "<br>" . $conn->error;
    }
    $conn->close();
}

?>
</body>
</html>

Link til toppen.

24.10 Opdater data

For at opdatere skal vi først hente data frem til brugeren. Det gør vi bedst i en formular. Bagefter gemmer vi det, vi har opdateret, lige som når vi opretter en ny post i databasen. Igen kan du kikke hos W3 Schools for den generelle kode: https://www.w3schools.com/php/php_mysql_update.asp

Del 1. Hent data og vis dem til brugeren

Du har allerede prøvet at hente data, men dengang gjorde vi det mere primitivt. Denne gang vil jeg gøre det pænere, dvs. i en tabel. Jeg opbygger en tabel (1) vha. noget snedig HTML kode + at jeg fletter HTML og PHP.

Jeg laver en formular om hver række i min tabel. Den starter inden <tr> (2) og den slutter efter </tr> (3). Faktisk er det ikke 100% rigtig HTML, men det virker.

Jeg placerer 2 knapper i hver linje (4) i tabellen. En knap, som brugeren ser og kan klikke på, som hedder “Ret”. Der er også knap, som er skjult. Den skjulte knap sender kunde-id for linjen videre til backend, så jeg kan identificere, hvilken linje jeg arbejder med. Den skjulte knap hedder input type = “hidden”.

Resultatet ser sådan ud i browseren:

Del 2. Opdater databasen

Del 2 ligner helt det, du kender, vi skal bare have et andet SQL kald ind. Samtidig skal vi kalde med parametre, dvs. vi skal sætte vores variable ind.

Hele hemmeligheden er, at jeg skal opdatere en bestemt række. Hver række har en unik værdi, som er primærnøglen. I eksemplet er det kunde-id, som er primærnøglen. Hvis jeg bare kan sætte den ind, så ved jeg, hvilken række jeg vil opdatere. I min kode har jeg hentet kunde-id’et fra mit skjulte input-felt som en variable, jeg kalder for “$kundeid”.

Her er hele koden:

<!DOCTYPE html>
<html>
<head>
    <title>Vis data fra en mysql database</title>
</head>
<body>
<h1>Opdater data</h1>
<?php
// opret forbindelse til serveren
$servername = "localhost";
$username = "Lars";
$password = "Db";
$dbname = "kundedatabase";

// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
    die("Connection failed: " . $conn->connect_error);
}

// DEL 1. HENT DATA OG VIS DET TIL BRUGEREN I EN TABEL MED INPUT FELTER
 
$sql = "SELECT `kunde_id`,`fornavn`,`efternavn`,`adresse`,`postnummer`,`telefonnummer`,`email` FROM `kunde`";
// hent resultatet ind i en variabel
$result = $conn->query($sql);

// tjek om der overhovedet er et antal rækker
if ($result->num_rows > 0) {
    // Hvis der er et antal rækker, så udskriv dem
    echo " 
    <table>
    <tr>
        <th>Kundeid</th>
        <th>Fornavn</th>
        <th>Efternavn</th>
        <th>Adresse</th>
        <th>Postnummer</th>
        <th>Telefonnummer</th>
        <th>Email</th>
        <th>&nbsp;</th>
    </tr>";
    while($row = $result->fetch_assoc()) {
        // bemærk, at vi blander html og php efter forgodtbefindende
        // bemærk også, at jeg viser data i input-felter. Det gør, at brugeren kan rette i felterne
        echo "<form method='post' action=''><tr>";
        echo "<td>". $row["kunde_id"]. "</td>";
        echo "<td><input type='text' name='fornavn' value='". $row["fornavn"]. "'></td>";
        echo "<td><input type='text' name='efternavn' value='". $row["efternavn"]. "'></td>";
        echo "<td><input type='text' name='adresse' value='".$row["adresse"]."'></td>";
        echo "<td><input type='text' name='postnummer' value='". $row["postnummer"]. "'></td>";
        echo "<td><input type='text' name='telefonnummer' value='". $row["telefonnummer"]. "'></td>";
        echo "<td><input type='text' name='email' value='". $row["email"]. "'></td>";
        echo "<td><input type='submit' value='Ret'><input type='hidden' name='kundeid' value=". $row["kunde_id"]. "</td>";
        echo "</tr></form>";
    }
    echo "</table>";
} else {
    echo "0 results";
}
//DEL 2. MODTAG DATA OG OPDATER DATABASEN

// hent alle variable fra formularen
if ($_SERVER["REQUEST_METHOD"] == "POST") {
    // opret og initialiser variablene

    $kundeid = $fornavn = $efternavn = $adresse = $postnummer = $telefonnummer = $email = 0;
    // hent data fra formularen
    $kundeid = $_POST["kundeid"];
    $fornavn = $_POST["fornavn"];
    $efternavn = $_POST["efternavn"];
    $adresse = $_POST["adresse"];
    $postnummer = $_POST["postnummer"];
    $telefonnummer = $_POST["telefonnummer"];
    $email = $_POST["email"];   
    update_data($kundeid, $fornavn, $efternavn, $adresse, $postnummer, $telefonnummer, $email);
    // den lille sætning i nedenunder opdaterer siden. Prøv at udkommatere den, så er du nødt til at genopfriske siden for at få vist den opdaterede database
    header("refresh: 0;");
  }
 
 function update_data($kundeid, $fornavn, $efternavn, $adresse, $postnummer, $telefonnummer, $email){
     
    $servername = "localhost";
    $username = "Lars";
    $password = "Db";
    $dbname = "kundedatabase";

    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    // Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    }
    
    // selve SQL kaldet
    
$sql = "UPDATE kunde SET fornavn= '$fornavn', efternavn = '$efternavn', adresse = '$adresse', postnummer= $postnummer, telefonnummer = '$telefonnummer', email= '$email' WHERE `kunde_id`= '$kundeid'";


    if ($conn->query($sql) === TRUE) {
        echo "Databasen er opdateret";
        echo "Hej verden";
    } else {
        echo "Der er opstået en fejl: " . $conn->error;
    }
    echo "Hej verden";
    $conn->close();
}
?>
 
</body>
</html> 

Link til toppen.

24.11 Slet data

Vi sletter meget nødig i databaser. Vi ønsker at bevare historikken og laver vi en fejl, så vil vi rette den frem for at slette. Lige som vi gør i regnskaber. Hvis man endelig sletter, så bør det være forbeholdt få personer i en organisation.

Hvad så, hvis en kunde, “Svend Snydepels” konstant nægter at betale sine regninger? Sletter vi så ham, når vi endelig efter at truet med Hells Angels får vores penge? Nej. Vi vil hellere gør ham til “passiv” kunde. Dvs. man laver et ja-nej felt, kalder feltet for aktiv/passiv og så sætter man flueben for at blokere for yderligere salg til ham. Det sikrer, at vi gemmer hans historik.

Hvis du vil kode en procedure, der kan slette, så ligner koden både hent, opret og opdater. Du kan se den hos W3 Schools på https://www.w3schools.com/php/php_mysql_delete.asp.

Link til toppen.