LC238D http://www.aitel.hist.no/fag/_dmdb/ SQL, del 1 - SELECT Hva er SQL? side 2 Eksempelbase side 3 SELECT-setningen, syntaks side 4-5 Operatorer side 6 Å hente ut et radintervall fra resultatsettet side 7 Tekster side 8 Sortering - Aggregeringsfunksjoner side 9 Å gruppere data side 10 Forening (join) side 11-14 Delspørringer side 15 ANY og ALL side 16 EXISTS side 17-18 Læreboka: Fra og med kap 4.3.2 side 120 fram til kap. 4.4 side 142 unntatt 4.3.4 side 122 Se også http://www.sqlzoo.net/ Forelesning 8
Hva er SQL? Opprinnelig SEQUEL (Structured English Query Language, IBM) Standard SQL 1986 (ANSI + ISO) - SQL1 Standard SQL 1992 - SQL2 (SQL1 + diverse join s, mm.) SQL3 (SQL2 + objektorientering, mm., se kap. 7 i læreboka) Et ikke-prosedyralt språk (hva og ikke hvordan) De fleste moderne DBMS har støtte for store deler av SQL2 Programmering av tjeneren, funksjoner, triggere siste del av kurset (JavaDB med Java) Mange dialekter (= et superset av et subset av standard) av SQL. Typiske forskjeller CREATE TABLE-syntaks Lovlige datatyper Datatyper for dato og klokkeslett Funksjoner for strengbehandling Se f.eks. http://troels.arvin.dk/db/rdbms/ Læreboka følger SQL-92-standarden, forelesninger og øvinger viser JavaDB-syntaks (og i noen tilfeller Oracle-syntaks) side 2
Eksempelbase leverandor lev_nr lev_navn status lev_by 1 Svendsen 20 Lillehammer 2 Jensen 10 Porsgrunn 3 Bø 30 Porsgrunn 4 Christiansen 20 Lillehammer 5 Andersen 30 Arendal produkt prod_nr prod_navn fargekode vekt prod_by 1 synåler rød 12 Lillehammer 2 binders grønn 17 Porsgrunn 3 skruer blå 17 Risør 4 skruer rød 14 Lillehammer 5 knapper blå 12 Porsgrunn 6 spiker rød 19 Lillehammer leveranse lev_nr prod_nr antall 1 1 300 1 2 200 1 3 400 1 4 200 1 5 100 2 1 300 2 2 400 3 2 200 4 2 200 4 4 300 4 5 400 Sqlscript vedlagt side 3
SELECT-setningen SELECT [DISTINCT ALL] { * { {table view}.* expr } [ [AS] c_alias ] [, { {table view snapshot}.* expr } [ [AS] c_alias ] ]... } FROM {table view} [t_alias] [, {table view} [t_alias] ]... [WHERE condition ] [GROUP BY expr [, expr]... [HAVING condition] ] [{UNION UNION ALL INTERSECT EXCEPT} SELECT command ] [ORDER BY {expr position} [ASC DESC][, {expr position} [ASC DESC]]...] [OFFSET { integer-literal? } {ROW ROWS} -- nytt i SQL-2011 FETCH { FIRST NEXT } [integer-literal? ] {ROW ROWS} ONLY] (ROW is synonymous with ROWS and FIRST is synonymous with NEXT) Eksempel: SELECT lev_navn FROM leverandor WHERE status < 25; Logisk skjer følgende: 1. Først beregnes alt fra og med FROM til og med HAVING. Resultatet er alltid en tabell T1, som er input til trinn 2: 2. En velger / beregner de kolonnene som er spesifisert etter SELECT. Resultatet er tabellen T2. 3. Dersom DISTINCT er spesifisert elimineres duplikater fra T2. Resultat T3. 4. Eventuell sortering. 5. Hent ut et intervall med rader fra resultatet Mengdeoperasjoner, UNION/INTERSECT/EXCEPT, gjennomgås ikke i dag, men se forelesning 3. side 4
* betyr alle kolonnene Hva kan vi ha i SELECT-listen? tabellnavn.* betyr alle kolonnene i en bestemt tabell kolonnenavn eller tabellnavn.kolonnenavn dersom kolonnenavnet ikke er entydig i de tabellene som er nevnt etter FROM uttrykk der verdien er et tall eller en tekststreng, vanligvis basert på kolonner. Kolonnenavn kan være argument til funksjoner (max, min, ) eller operand til operatorer (+ - osv.), Kolonner kan ha alias: SELECT p.prod_navn, lo.lev_navn, lev.antall FROM produkt p, leveranse lev, leverandor lo WHERE p.prod_nr = lev.prod_nr AND lo.lev_nr = lev.lev_nr; side 5
Operatorer Sammenlikningsoperatorer: = > < >= <= <> BETWEEN IN BETWEEN inkluderer grensene Aritmetiske operatorer: + - * / / utfører heltallsdivisjon hvis operandene er heltall Sammensatte logiske uttrykk lages v.h.a. AND, OR og NOT. AND har høyere prioritet enn OR slik det også er i Java Parenteser kan brukes til å overstyre prioritetene. Eksempler: 1. Finn produkter med fargekode rød og som veier mindre enn 15 gram SELECT * FROM produkt WHERE kode = 'rød' AND vekt < 15; 2. Skriv ut produktvekt i kilo SELECT prod_nr, prod_navn, vekt*0.001 "Vekt i kilo" FROM produkt; 3. Finn produkter som veier mellom 14 og 17 gram SELECT * FROM produkt WHERE vekt BETWEEN 14 AND 17; 4. Finn leveranser på enten 100, 200 eller 400 enheter SELECT * FROM leveranse WHERE antall IN (100, 200, 400); 5. Finn leveranser som verken er 100 eller 200 enheter SELECT * FROM leveranse WHERE antall NOT IN(100, 200); side 6
Å hente ut et radintervall fra resultatsettet Kan hente ut kun et radintervall fra resultatsettet: OFFSET { integer-literal? } {ROW ROWS} FETCH { FIRST NEXT } [integer-literal? ] {ROW ROWS} ONLY Eksempler: Finn de fem største leveransene. Hvis flere leveranser kommer på 5.plass er det nok med én. SELECT * FROM leveranse ORDER BY antall desc fetch first 5 rows only; Som foran, men start på den nest største leveransen. SELECT * FROM leveranse ORDER BY antall desc offset 1 rows fetch next 5 rows only; Hent ut kun en leverandør. SELECT * FROM leveranse ORDER BY antall desc fetch first 1 row only; side 7
Tekster Æ, ø og å bør alltid prøves ut med den installasjonen du bruker Sjekk omforming mellom store og små bokstaver Sjekk sortering (order by, sammenlikningsoperatorene) Oracle ok JavaDB: Bruk databaseurl (eksempel): jdbc:derby://localhost:1527/persondata;territory=no_no; collation=territory_based;user=vprg;password=vprg Fungerer fint fra Java (JDBC), problematisk i NetBeans Tekster kan skjøtes med operatoren SELECT lev_navn ' er fra ' lev_by ' og har status ' cast(status as char(10)) FROM leverandor; (casting er nødv. i JavaDB, trengs vanligvis ikke) Søker i tekst med = > < >= <= <> BETWEEN IN Kan søke på deler av ord med operatoren LIKE og jokertegnene _ og % Navn som slutter på 'sen : SELECT lev_navn FROM leverandor WHERE lev_navn like '%sen'; Navn på to tegn: SELECT lev_navn FROM leverandor WHERE lev_navn like ' '; Navn med S/s i: SELECT lev_navn FROM leverandor WHERE upper(lev_navn) like '%S%'; Funksjonene LOWER() og UPPER() omformer til små/store bokstaver. INITCAP() omformer bare første bokstav (Oracle ok, JavaDB ikke ok) side 8
Sortering: Sortering Skriv ut leverandørdata primært sortert etter by, sekundært etter navn SELECT * FROM leverandor ORDER BY lev_by, lev_navn; Skriv ut produktinformasjon for produkter med fargekode rød, ordnet etter avtagende vekt. SELECT * FROM produkt WHERE kode = 'rød' ORDER BY vekt desc; side 9
Aggregeringsfunksjoner Aggregeringsfunksjoner: COUNT(), AVG(), MAX(), MIN(), SUM() Hvor mange leverandører har vi? SELECT COUNT(*) "antall" FROM leverandor; Hvor mye leveres totalt av produkt 2? SELECT SUM(antall) "sum" FROM leveranse WHERE prod_nr = 2; Utfør alle funksjonene for leveranser av produkt 2 SELECT COUNT(*) "antall", SUM(antall) "sum", AVG(antall) "gj.snitt", MAX(antall) "maks", MIN(antall) "min" FROM leveranse WHERE prod_nr = 2; side 10
Å gruppere data SELECT ting FROM tabell(er) [WHERE betingelse] GROUP BY grupperingskolonne(r) HAVING betingelse; ting må være en av grupperingskolonnene eller en av aggregeringsfunksjonene COUNT, SUM, AVG, MAX, MIN HAVING brukes til å begrense gruppene Bland ikke sammen WHERE og HAVING. WHERE begrenser enkeltrader, HAVING begrenser grupper. 1. Hvor mye leveres av hvert enkelt produkt? SELECT prod_nr, SUM(antall) "sum" FROM leveranse GROUP BY prod_nr; 2. Som 1, men ta bare med de produktene der summen er over 500 SELECT prod_nr, SUM(antall) "sum" FROM leveranse GROUP BY prod_nr HAVING SUM(antall) > 500; 3. Hvilke produkter leveres av eksakt én leverandør? SELECT prod_nr FROM leveranse GROUP BY prod_nr HAVING COUNT(*) = 1; side 11
Forening (join) Forening betyr å hente data fra mer enn en tabell, det kartesiske produkt er basis. SELECT lev_navn, leverandor.lev_nr, prod_nr, antall FROM leverandor, leveranse; gir 5 x 11 rader Begrenser til rader med samme verdi på et felt fra hver av tabellene (naturlig- eller likhets-forening), vanligvis primærnøkkel fremmednøkkel SELECT * FROM leverandor, leveranse WHERE leverandor.lev_nr = leveranse.lev_nr; Ekvivalent med bruk av det reserverte ordet JOIN (evt. INNER JOIN) SELECT * FROM leverandor JOIN leveranse ON (leverandor.lev_nr = leveranse.lev_nr); --- SELECT * FROM leverandor NATURAL JOIN leveranse; side 12
Mer om likhetsforening (equijoin) Finn leverandører og produkter som er lokalisert i samme by. SELECT * FROM leverandor, produkt WHERE leverandor.lev_by = produkt.prod_by; Ønsker å få ut leveransetabellen, men med opplysninger om navn på leverandør og produkt. SELECT le.lev_nr,l.lev_navn,le.prod_nr,p.prod_navn, le.antall FROM leveranse le NATURAL JOIN leverandor l NATURAL JOIN produkt p; side 13
Ytter-forening (outer join) Alle leverandører, også de som ikke har leveranser SELECT * FROM leverandor LEFT OUTER JOIN leveranse ON (leverandor.lev_nr = leveranse.lev_nr); Finn leverandører og produkter som er lokalisert i samme by. Ta også med leverandørbyer der det ikke er produkter. SELECT * FROM leverandor LEFT OUTER JOIN produkt ON (leverandor.lev_by = produkt.prod_by); Finn leverandører og produkter som er lokalisert i samme by. Ta også med produktbyer der det ikke er leverandører. SELECT * FROM leverandor RIGHT OUTER JOIN produkt ON (leverandor.lev_by = produkt.prod_by); Kombiner de to foran FULL OUTER JOIN SELECT leverandor.lev_by, produkt.prod_by FROM leverandor LEFT OUTER JOIN produkt ON (leverandor.lev_by = produkt.prod_by) UNION SELECT leverandor.lev_by, produkt.prod_by FROM leverandor RIGHT OUTER JOIN produkt ON (leverandor.lev_by = produkt.prod_by); Hvis en bare skal ha ut alle byene: SELECT lev_by FROM leverandor UNION SELECT prod_by FROM produkt; side 14
Å forene en tabell med seg selv ( selfjoin ) Sett opp en oversikt over to og to leverandører som er stasjonert i samme by SELECT lev1.lev_navn, lev2.lev_navn FROM leverandor lev1, leverandor lev2 WHERE lev1.lev_by = lev2.lev_by; leverandor lev1 lev_nr lev_navn status lev_by 1 Svendsen 20 Lillehammer 2 Jensen 10 Porsgrunn 3 Bø 30 Porsgrunn 4 Christiansen 20 Lillehammer 5 Andersen 30 Arendal leverandor lev2 lev_nr lev_navn status lev_by 1 Svendsen 20 Lillehammer 2 Jensen 10 Porsgrunn 3 Bø 30 Porsgrunn 4 Christiansen 20 Lillehammer 5 Andersen 30 Arendal side 15
Delspørringer (SUB-SELECT) Kan av og til være et alternativ til forening. Finn navnene til de som leverer produkt 2. Forening: SELECT lev_navn FROM leverandor NATURAL JOIN leveranse WHERE prod_nr = 2; Delspørring: SELECT lev_navn FROM leverandor WHERE lev_nr IN (SELECT DISTINCT lev_nr FROM leveranse WHERE prod_nr = 2); Finn leverandørnummer og -navn til leverandørene som har statusverdi mindre enn maksimalverdien akkurat nå SELECT lev_nr, lev_navn FROM leverandor WHERE status < (SELECT max(status) FROM leverandor); Eksakt én verdi etter < > == <> >= <= Syntaksfeil: SELECT lev_nr, lev_navn FROM leverandor WHERE status <max(status); side 16
ANY (SOME) og ALL Brukes sammen med = > < >= <= <> foran delspørringer som returnerer én kolonne som svar Eksempler: Finn hvilke produkter som har nummer større enn alle produktene som leveres av leverandør 2 SELECT * FROM produkt WHERE prod_nr > ALL(SELECT prod_nr FROM leveranse WHERE lev_nr = 2); Finn hvilke produkter som har nummer større enn et hvilket som helst av produktene som leveres av leverandør 2 SELECT * FROM produkt WHERE prod_nr > ANY(SELECT prod_nr FROM leveranse); Hvis resultatet av delspørringen er en tom mengde ANY returnerer false ALL returnerer true side 17
EXISTS i SELECT-setningen Finn navnene til de som leverer produkt 2. Forening: SELECT lev_navn FROM leverandor NATURAL JOIN leveranse WHERE prod_nr = 2; Delspørring: SELECT lev_navn FROM leverandor WHERE lev_nr IN (SELECT distinct lev_nr FROM leveranse WHERE prod_nr = 2); En tredje mulighet, delspørring med EXISTS SELECT distinct lev_navn FROM leverandor WHERE EXISTS (SELECT * FROM leveranse WHERE prod_nr = 2 AND leveranse.lev_nr = leverandor.lev_nr); side 18
EXISTS, forts Finn navnene til dem som ikke leverer produkt nr 2 NOT EXISTS SELECT distinct lev_navn FROM leverandor WHERE NOT EXISTS (SELECT * FROM leveranse WHERE prod_nr = 2 AND leveranse.lev_nr = leverandor.lev_nr); NOT IN SELECT distinct lev_navn FROM leverandor WHERE lev_nr NOT IN (SELECT lev_nr FROM leveranse WHERE prod_nr = 2); Finn navnene til dem som leverer alle produktene = divisjon i relasjonsalgebra: leveranse dividert med produkt.prod_nr SELECT lev_navn FROM leverandor WHERE not exists (SELECT * FROM produkt WHERE not exists (SELECT * FROM leveranse WHERE leveranse.lev_nr = leverandor.lev_nr and leveranse.prod_nr = produkt.prod_nr)); dvs.: Velg leverandører slik at det ikke fins produkter som de ikke levererer. Alternativt i dette tilfellet: SELECT lev_navn FROM leverandor WHERE (SELECT count(*) FROM leveranse WHERE leveranse.lev_nr = leverandor.lev_nr) = (SELECT count(*) FROM produkt); side 19