När en webbshop ska märka order som ”liten”, ”normal” eller ”stor” kan SQL göra jobbet direkt i frågeresultatet. CASE passar oftast när en fråga ska ge olika värden beroende på villkor, men utfallet påverkas av ordningen på villkoren och hur NULL hanteras.
CASE gör villkor till värden i frågeresultatet
- Två former: enkel CASE för likhetsjämförelser och sökt CASE för villkor som intervall.
- Första träffen gäller: villkoren prövas i ordning, så placera de mest specifika först.
- ELSE saknas: om inget villkor matchar blir resultatet normalt NULL.
- Datatyper måste fungera ihop: resultatgrenarna behöver kunna förenas till en gemensam typ.
Välj sökt CASE när villkoret handlar om mer än likhet
En order på 1 200 kronor kan hamna i en annan kategori än en order på 300 kronor, trots att båda ligger i samma kolumn. Därför tycker jag att sökt CASE är det tydligaste valet när logiken bygger på intervall eller flera jämförelser.
Den sökta formen utgår från ett eller flera villkor. Varje WHEN testar en logisk uttrycksdel, och motsvarande THEN anger värdet som ska returneras:
SELECT
order_id,
total_amount,
CASE
WHEN total_amount >= 1000 THEN 'Stor'
WHEN total_amount >= 300 THEN 'Normal'
ELSE 'Liten'
END AS orderstorlek
FROM orders;Här hamnar en order på 1 200 i kategorin ”Stor”. En order på 500 blir ”Normal”. Eftersom villkoren prövas uppifrån och den första sanna grenen väljs, ska gränsen 1 000 stå före gränsen 300.
Enkel CASE är renare när du bara jämför värden
Om en kolumn bara ska översättas från fasta koder till begripliga etiketter blir enkel CASE ofta mer lättläst. Tänk på en webbplats där statuskoder ska visas som text:
SELECT
order_id,
CASE status
WHEN 'new' THEN 'Ny'
WHEN 'paid' THEN 'Betald'
WHEN 'sent' THEN 'Skickad'
ELSE 'Okänd status'
END AS status_text
FROM orders;Den här formen jämför uttrycket efter CASE med varje värde efter WHEN. Den passar för likhet, men inte för exempelvis ”större än” eller ett beloppsintervall. I sådana fall väljer du sökt CASE, som beskrivs i PostgreSQLs dokumentation om villkorsuttryck.
Villkorsordningen avgör vilken kategori som visas
Två villkor kan passa samma rad, men bara det första matchande resultatet används. Det låter enkelt tills en bred regel råkar ligga före en smalare.
Anta att ”premium” ska gälla för kunder med minst tio köp, medan ”kund” gäller för alla med minst ett köp. Om den breda regeln står först kan premiumkunder få etiketten ”kund”. Lägg därför mer avgränsade villkor före mer generella.
| Situation | Trolig följd | Gör så här |
|---|---|---|
| En bred gräns står före en högre gräns | Den breda grenen fångar även värden som skulle ha fått en mer specifik etikett. | Flytta den snävare gränsen högre upp. |
| Inget villkor matchar och ELSE saknas | Resultatet blir NULL, vilket kan ge tomma etiketter eller oväntade grupper. | Lägg till ELSE om varje rad ska få ett synligt standardvärde. |
| En kolumn kan innehålla NULL | En vanlig jämförelse med NULL blir inte sann. | Testa uttryckligen med IS NULL i sökt CASE. |
| THEN- och ELSE-grenar returnerar oförenliga datatyper | Databasen kan försöka konvertera värden eller ge ett fel. | Se till att grenarna ger kompatibla typer, till exempel enbart text eller enbart numeriska värden. |
Den där NULL-raden är lätt att missa. Skriv exempelvis WHEN status IS NULL THEN 'Saknas', inte WHEN status = NULL. SQL behandlar NULL som ett okänt värde, så en vanlig likhetsjämförelse fångar det inte.
CASE väljer ett värde, inte nästa SQL-åtgärd
Namnet ”CASE statement” används ofta i vardagligt tal, men i vanliga SQL-frågor är CASE ett uttryck som returnerar ett värde. Det kan därför passa i SELECT, ORDER BY och andra sammanhang där ett uttryck är tillåtet, men det ersätter inte ett procedurflöde som kör olika satser.
Det är en användbar skillnad när en fråga växer. CASE kan till exempel välja vilken etikett en rad får eller vilken sorteringsprioritet som visas. Om du behöver köra olika block med SQL-kod beroende på villkor ska du i stället använda det procedurspråk som databasen erbjuder. Microsoft beskriver samma gräns för CASE i Transact-SQL.
Gör resultatet förutsägbart med ELSE och gemensamma typer
En kategori som ibland blir tom är ofta en följd av att CASE saknar ELSE. När ingen WHEN matchar returneras NULL om ingen standardgren har angetts. Jag tycker att ett uttryckligt ELSE är säkrast när rapporter, etiketter eller grupper ska vara begripliga även för oväntade värden.
Grenarnas resultat behöver också kunna bli en gemensam datatyp. Blanda exempelvis inte en textetikett i en gren med ett datum i en annan och räkna med att varje databasmotor tolkar det likadant. Om CASE ger ett fel eller konstiga konverteringar, kontrollera först datatyperna i THEN och ELSE.
Det finns dessutom en viktig gräns för kortslutning: även om CASE testar villkor i ordning ska du inte använda den som skydd mot varje beräkningsfel. SQL Server dokumenterar att vissa uttryck, bland annat aggregat i WHEN-villkor, kan beräknas innan CASE-villkoret utvärderas. Bygg därför inte säkerheten i en riskfylld beräkning på antagandet att en tidigare gren alltid stoppar den.
En bra CASE börjar med rätt fråga om varje rad
Föreställ dig en rapport som ska sortera order efter belopp, men där saknade belopp också måste synas. Börja med att bestämma vad NULL ska betyda, lägg sedan de högsta och mest specifika gränserna först och avsluta med en ELSE som beskriver återstående fall.
När utfallet fortfarande överraskar är det ofta enklare att tillfälligt visa både originalkolumnen och CASE-resultatet i samma SELECT. Då ser du exakt vilken rad som fångas av fel villkor, i stället för att gissa utifrån den färdiga etiketten.