Hopp til innhold

SQL

språket relasjonsdatabaser svarer på

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:

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 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 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

Ordliste