Dette modul dækker underforespørgsler, uomstødelig sikkerhed via databasetransaktioner samt performanceoptimering (indeksering).
Lektion 13: Subqueries (Underforespørgsler) og Views
1. Subqueries (Forespørgsler inde i forespørgsler)
Nogle gange har du brug for at hente data baseret på en dynamisk beregning, du ikke kender på forhånd. Her kan du placere en SELECT-forespørgsel inde i en anden SELECT-forespørgsel.
-- Eksempel: Hent alle brugere, der har skrevet flere indlæg end gennemsnittet
SELECT navn FROM brugere
WHERE id IN (
SELECT bruger_id FROM indlaeg
GROUP BY bruger_id
HAVING COUNT(id) > (SELECT COUNT(id) / COUNT(DISTINCT bruger_id) FROM indlaeg)
);
2. Views (Virtuelle tabeller)
Hvis du har en gigantisk SQL-query med mange JOINs og funktioner, som du skal bruge 20 forskellige steder i dit PHP-projekt, kan du gemme forespørgslen direkte i MariaDB som et View. Det fungerer som en virtuel tabel, du kan lave almindelige SELECT-kald imod.
-- Kør dette i phpMyAdmin for at gemme en kompleks query under navnet 'v_blog_detaljer'
CREATE VIEW v_blog_detaljer AS
SELECT i.id, i.titel, b.navn AS forfatter, COUNT(i.id) AS forfatter_total_indlaeg
FROM indlaeg i
INNER JOIN brugere b ON i.bruger_id = b.id
GROUP BY i.id;
Nu bliver dit PHP-kode ekstremt simpelt og letlæseligt:
// I PHP skal du nu bare kalde dit View som en helt almindelig tabel:
$stmt = $pdo->query("SELECT * FROM v_blog_detaljer WHERE forfatter = 'Anna Hansen';");
Lektion 14: Transaktioner (ACID-principper)
Forestil dig, at du bygger en webshop eller et banksystem. En bruger køber en vare. Du skal gøre to ting i databasen:
- Trække penge fra kundens saldo (
UPDATE kunder ...).
- Trække 1 fra varens lagerbeholdning (
UPDATE produkter ...).
Hvis serveren mister strømmen præcis efter trin 1, men før trin 2, har du trukket kundens penge uden at opdatere lageret. Data er nu korrupt.
Løsningen er transaktioner. En transaktion sikrer, at en række SQL-kald enten lykkes 100% sammen, eller overhovedet ikke udføres (alt rulles tilbage).
<?php
// transaktion_ordre.php
require_once 'db_forbindelse.php';
$kundeId = 4;
$produktId = 12;
$pris = 299;
try {
// 1. Fortæl MariaDB at vi starter en lukket transaktion
$pdo->beginTransaction();
// 2. SQL-kald A: Træk pengene fra kunden
$stmtA = $pdo->prepare("UPDATE kunder SET saldo = saldo - :pris WHERE id = :id;");
$stmtA->execute(['pris' => $pris, 'id' => $kundeId]);
// 3. SQL-kald B: Træk varen fra lageret
$stmtB = $pdo->prepare("UPDATE produkter SET lager = lager - 1 WHERE id = :id;");
$stmtB->execute(['id' => $produktId]);
// 4. Hvis vi er nået hertil uden fejl, gemmes alle ændringer permanent i databasen
$pdo->commit();
echo "Ordre gennemført! Databasen er opdateret korrekt.";
} catch (Exception $e) {
// Hvis blot ÉT af SQL-kaldene fejler, rammer vi denne catch-blok.
// Vi ruller ALT tilbage, så ingen ændringer gemmes (Database-salvation!)
$pdo->rollBack();
echo "Ordre fejlede. Alle ændringer er rullet tilbage: " . $e->getMessage();
}
Lektion 15: Indeksering og optimering (Performance)
Når du skriver SELECT * FROM brugere WHERE email = 'test@test.dk';, skal MariaDB som standard løbe samtlige rækker igennem fra top til bund for at finde matchet. Hvis du har 2 millioner brugere, tager det lang tid.
Et indeks (INDEX) fungerer som et stikordsregister i bunden af en tyk bog. Databasen opretter en sorteret søgestruktur i baggrunden, så den kan finde rækken med det samme (et opslag falder typisk fra flere sekunder til få millisekunder).
1. Oprettelse af et indeks
Du bør sætte et indeks på de kolonner, du meget ofte bruger i dine WHERE-betingelser eller i dine JOINs.
-- Gør søgninger på e-mail lynende hurtige (Unique sikrer samtidig at to ikke kan have samme e-mail)
CREATE UNIQUE INDEX idx_bruger_email ON brugere(email);
-- Sæt almindeligt indeks på en kolonne der ofte søges eller sorteres efter
CREATE INDEX idx_indlaeg_oprettet ON indlaeg(oprettet_at);
2. Analysér din kode med EXPLAIN
Hvis du har en langsom forespørgsel, kan du sætte ordet EXPLAIN foran din SQL-kode i phpMyAdmin:
EXPLAIN SELECT * FROM brugere WHERE email = 'anna@example.dk';
MariaDB vil nu returnere en tabel, der forklarer dens søgestrategi. Hold øje med kolonnen type (hvis der står ALL, betyder det fuld tabelscanning – her mangler du et indeks) og key (fortæller hvilket indeks databasen rent faktisk brugte).