# SQL

## Hva det er

SQL er et deklarativt språk for å definere, endre og lese data som ligger i
relasjonelle tabeller: du skriver ned resultatet du vil ha, og databasen
bestemmer hvordan det skal lages. Bokstavene står vanligvis for Structured Query
Language og uttales enten bokstav for bokstav eller som det engelske «sequel»,
mens den internasjonale standarden som definerer språket, ISO/IEC 9075, bare
kaller det Database Language SQL.

Deklarativ er ordet som bærer vekten. Setningen nedenfor navngir en tabell, en
betingelse, en gruppering og en rekkefølge. Den sier ingenting om indekser,
løkker eller hvilke rader som skal leses først:

```sql
SELECT country, count(*) AS signups
FROM accounts
WHERE created_at >= now() - interval '30 days'
GROUP BY country
ORDER BY signups DESC;
```

I PostgreSQL går den teksten gjennom fire trinn. En parser sjekker syntaksen og
bygger et tre, et omskrivingstrinn folder ut eventuelle visninger setningen
nevner, en planlegger sammenligner de mulige måtene å kjøre den på, f.eks. et
sekvensielt søk mot en indeks, og en utfører henter til slutt radene. Den samme
setningen kan ta et millisekund eller et minutt, alt etter hva planleggeren
finner, og derfor hører det å lese en plan med til å skrive SQL.

Språket har deler som folk gir egne navn. Datadefinisjon oppretter og endrer
tabeller, skjemaer og indekser. Datamanipulering setter inn, oppdaterer, sletter
og fletter rader. Spørringer leser dem med SELECT. Rettigheter og transaksjoner
avgjør hvem som får gjøre hva, og hvilke endringer som lander samtidig. Alt
dette reiser som tekst over én enkelt tilkobling, så SQL er fellesgrunnen
mellom en applikasjon, et rapportverktøy og et menneske ved en terminal.

Standarden er revidert mange ganger, sist som SQL:2023, og hver motor
implementerer sin egen delmengde av den. Vedlegget om samsvar i
PostgreSQL-dokumentasjonen oppgir at PostgreSQL oppfyller minst 170 av de 177
obligatoriske Core-funksjonene, og at ingen databasesystemer hevdet fullt
samsvar med Core da teksten ble skrevet. Hver motor legger også til en egen
dialekt: Microsoft SQL Server snakker Transact-SQL, BigQuery snakker GoogleSQL,
og PostgreSQL legger til PL/pgSQL for prosedyrekode som kjører inne i
databasen.

## Hvorfor det er viktig

Beslutningen SQL legger foran et team, er hvor arbeidet med dataene skal skje:
inne i databasen, i språket den er bygd for å utføre, eller i applikasjonskode
som henter rader og behandler dem selv.

En mengdebasert setning kjører ved siden av dataene. Å filtrere en million
rader ned til førti inne i databasen sender førti rader over nettverket, mens
filtrering i applikasjonen sender en million og kaster de fleste.
Sammenkoblinger, aggregeringer og fjerning av duplikater følger samme
regnestykke. Å flytte den typen arbeid inn i SQL er ofte den billigste
hastighetsøkningen et tregt system har tilgjengelig, fordi den kjører på
databasen som allerede står der.

Eierskapet er motvekten. Logikk skrevet i SQL lever i migreringer og
funksjoner, som trenger den samme gjennomgangen, de samme testene og den samme
versjonskontrollen som all annen kode, og færre utviklere leser en lang
PL/pgSQL-funksjon like flytende som de leser Go eller Python. En fordeling som
fungerer, sender alt som må se mange rader, til databasen, og holder regler som
endrer seg med forretningen, i applikasjonen, der de er lettere å teste hver
for seg.

Flyttbarhet er den andre avveiningen. Kjernen i språket, SELECT med joins,
GROUP BY og ORDER BY, flytter mellom motorer med få endringer. Kantene flytter
dårlig: datoregning, upserts, paginering, JSON-funksjoner og prosedyrekode
varierer fra motor til motor, og en migrering fra én database til en annen
bruker mesteparten av innsatsen der. Å velge en motor er også å velge en
dialekt, og jo mer logikk som bor i den dialekten, desto dyrere blir en senere
flytting.

Rekruttering er den tredje. SQL er en av få ferdigheter som analytikere,
backendutviklere, dataingeniører og databaseadministratorer har felles, så et
datalag skrevet i ren SQL kan leses og gjennomgås av hvem som helst av dem. En
ORM skjuler SQL-en helt til en side blir treg, og da må noen likevel lese det
den genererte.

## Slik ser det ut i praksis

På én plattform setter PostgreSQL-funksjoner sammen hvert API-svar som et
komplett JSON-dokument, og Go-handleren sender det videre som `json.RawMessage`
uten å pakke det ut eller kode det på nytt.
[JSON-passthrough-arkitekturen](/nb/portfolio/designed-a-json-passthrough-architecture-where-postgresql-functions-58/)
gjør formingen av svaret til en SQL-oppgave, utført der dataene allerede ligger.
En liten QueryJSON-hjelper gjorde det mønsteret til det enkleste å følge, og
DTO-ene, mappingkoden og respons-structene som et lagdelt API vanligvis samler
opp, forsvant. Handlerkoden ble mye kortere, og en endring i hva en funksjon
returnerer, når klienten uten at noen redigerer en handler.

Det samme språket ser annerledes ut i en datapipeline. På tvers av
[seks ETL/ELT-pipeliner](/nb/portfolio/designed-implemented-and-administered-6-etl-elt-pipelines-8/)
som spente over BigQuery, MSSQL og PostgreSQL, ble rå filer lastet inn i
staging-skjemaer med bcp, psql og SSIS, og rensingen, normaliseringen og
berikingen ble skrevet som prosedyrer i PL/pgSQL og T-SQL. Tre motorer, hver med
sin dialekt, i én jobb: spørsmålet om flyttbarhet ovenfor, sett innenfra. Siden
måler resultatet for hele pipelinen, SQL-trinnene medregnet: en jobb på mer enn
tre timer for hånd tar nå under 20 minutter, dataene oppdateres daglig i stedet
for ukentlig, og Python-API-ene som leser dem, har fått 30 % bedre ytelse.

## Hvor det går galt

**Setninger limt sammen av strenger.** En kunde som heter `O'Brien`, får en
søkeside til å feile, eller en nøye tilpasset verdi i et skjemafelt leser andres
data. Årsaken er det brukeren skrev, limt rett inn i SQL-teksten, der databasen
har mistet evnen til å skille data fra kommando. Send verdiene som parametere i
stedet, atskilt fra setningen, slik hver utbredt driver tillater. I PostgreSQLs
eget klientbibliotek godtar et parameterisert kall dessuten bare én kommando per
streng, noe som gir et ekstra forsvar mot injeksjon.

**Rekkefølge som bare var flaks.** En liste som kom tilbake sortert i månedsvis,
kommer i tilfeldig rekkefølge etter en datainnlasting eller en ny plan. Mangler
ORDER BY, returnerer PostgreSQL radene i den rekkefølgen som er raskest å
produsere, og en LIMIT uten ORDER BY kan til og med gi et annet utvalg hver
gang. Hvert resultat der rekkefølgen betyr noe, må si det i spørringen.

**NULL behandlet som en verdi.** Et filter skrevet `WHERE discount = NULL`
returnerer ingenting, og `NOT IN` mot en underspørring som inneholder en NULL,
returnerer heller ingenting. En sammenligning med NULL gir ukjent i stedet for
sann eller usann, og en WHERE-klausul beholder bare radene der betingelsen er
sann. Skriv `IS NULL`, og foretrekk `NOT EXISTS` framfor `NOT IN` når den indre
kolonnen kan inneholde NULL.

**Dialekt som lekker inn i testene.** En testsuite består mot SQLite, og den
samme koden feiler mot PostgreSQL i produksjon, eller en flytting til SQL Server
snubler på hver `LIMIT`, som den motoren skriver `TOP` eller `OFFSET ... FETCH`.
Hver motor godtar ting de andre avviser. Test mot motoren du produksjonssetter,
og hold motorspesifikk syntaks på så få steder som mulig.

## Les mer

- [Part II. The SQL Language](https://www.postgresql.org/docs/current/sql.html)
  — PostgreSQLs fullstendige innføring i og referanse for språket, fra syntaks
  til råd om ytelse.
- [Appendix D. SQL Conformance](https://www.postgresql.org/docs/current/features.html)
  — delene av ISO/IEC 9075 og hvilke funksjoner PostgreSQL implementerer.
- [Transact-SQL Reference](https://learn.microsoft.com/en-us/sql/t-sql/language-reference)
  — dialekten i SQL Server, dokumentert av Microsoft.
- [Introduction to SQL in BigQuery](https://cloud.google.com/bigquery/docs/introduction-sql)
  — GoogleSQL og den eldre dialekten den erstattet.
- [Query Language Understood by SQLite](https://www.sqlite.org/lang.html)
  — syntaksen til en innebygd motor, setning for setning.

<https://engineer.company/nb/glossary/sql/>
