12
Tags:
databaser
Skrevet af
Bruger #4522
@ 06.02.2008
SELECT
Hvis du absolut skal rage til dig: Alt om den mægtige *Som du sikkert ved kan du vælge alle kolonner fra en tabel ved at bruge stjernesymbolet (*), som på både engelsk og dansk hedder asterisk (fra det latinske
astrum; ja, man lærer noget nyt hver dag). Grundlæggende er asterisken blot en genvej for alle kolonner. Med udgangspunkt i
Builders-tabellen er følgende to SELECT udsagn altså ens:
SELECT *
FROM Builders
SELECT BuilderId, Name
FROM Builders
I vores eksempel med
Builders-tabellen har vi kun to kolonner, men det er klart at hvis man har rigtig mange kolonner i en tabel er asterisken en god genvej og kan spare én for en masse tegn.
Hvis FROM bisætningen benævner to eller flere tabeller, kan det være nødvendigt at kvalificere asterisken med et tabelnavn - alt afhængig af hvilken information du ønsker at se i resultatmængden.
Hvis du f.eks. ønsker at se alle kolonnerne fra
Builders-tabellen for de værfter som har bygget skibene i
Ships-tabellen skal du bruge følgende SELECT:
SELECT DISTINCT Builders.*
FROM Builders
INNER JOIN Ships ON Builders.BuilderId = Ships.Builder
Her har vi puttet
Builders foran asterisken for at sørge for at vi kun får kolonnerne fra
Builders-tabellen (og vi bruger DISTINCT for at sikre at hver værft kun opstår én gang i resultatmængde. Eksitensen af DISTINCT i SQL viser at SQL her faktisk bryder med matematikens koncept om en mængde. I matematikken kan en mængde ikke indeholde samme objekt/genstand/whatever flere gange - undtaget er multimængder, men det er noget andet. Visse databasepurister, heriblandt guruen C. J. Date [hvis skriverrier varmt kan anbefales] nævner dette som et af eksemplerne på problemerne med de nuværende implementationer af den relationelle databasemodel. Dette blev en meget lang bisætning gemt væk i en parantes så lad os fortsætte med det planlagte program).
Resultatet af ovenstående SELECT er:
BuilderId Name
--------- -----------------------------------------------------
0 Dudgeon, Poplar, London
1 Chatham Dockyard
2 Armstrongs, Newcastle upon Tyne
3 Pembroke Dockyard
4 Kure Naval Yards, Japan
5 Thames Ironworks and Shipbuilding Co. Ltd
6 Armstrong-Whitworth, Elswick
7 Napier shipyards
8 John Brown and Company
9 Scotts' shipyard, Greenock
Hvis vi nu ikke have specificeret
Builders.*, men i stedet blot havde nøjes med asterisken, så havde resultatet været:
BuilderId Name Name Builder LaunchDate Length Engine Speed Draught
--------- ------------------------------------------ ------------------------------------ ------- ----------------------- ------ ------ ---------------------- ----------------------
0 Dudgeon, Poplar, London HMS Abyssinia (1870) 0 1905-01-24 00:00:00.000 68 0 9,59 4,45
1 Chatham Dockyard HMS Africa (1905) 1 1905-02-24 00:00:00.000 138 1 18 8,2
1 Chatham Dockyard HMS Agamemnon (1879) 1 1905-01-27 00:00:00.000 91 2 13 7,16
2 Armstrongs, Newcastle upon Tyne HMS Agincourt (1913) 2 1905-03-06 00:00:00.000 205 3 22,4 8,2
3 Pembroke Dockyard HMS Ajax (1880) 3 1905-02-11 00:00:00.000 91 2 13 7,16
9 Scotts' shipyard, Greenock HMS Ajax (1912) 9 1905-03-04 00:00:00.000 182 3 21,5 8,4
4 Kure Naval Yards, Japan Japanese battleship Aki 4 1905-03-05 00:00:00.000 146 4 20 8,4
1 Chatham Dockyard HMS Albemarle (1901) 1 1905-03-09 00:00:00.000 143 5 19 7,5
5 Thames Ironworks and Shipbuilding Co. Ltd HMS Albion (1898) 5 1905-02-15 00:00:00.000 131 5 18 7,9
1 Chatham Dockyard HMS Alexandra (1875) 1 1905-02-08 00:00:00.000 99 6 15,09 8
6 Armstrong-Whitworth, Elswick Chilean battleship Almirante Latorre 6 1905-02-19 00:00:00.000 190 7 22,75 10
3 Pembroke Dockyard HMS Anson (1886) 3 1905-02-11 00:00:00.000 100 6 15,7 8,48
7 Napier shipyards HMS Audacious (1869) 7 1905-01-15 00:00:00.000 86 9 13,5 7
8 John Brown and Company HMAS Australia (1911) 8 1905-02-20 00:00:00.000 179 3 25 9,1
Som det ses får vi her alle kolonnerne fra de to tabeller der er benævnt i sætningens JOIN del (og vi bruger JOIN fordi vi kun ønsker
Builder information fra de værfter som har bygget de skibe vi har i vores base, hvorfor værftet med
BuilderId = 10 ikker er med da de ikke har bygget nogle af vores skibe).
SELECT uden brug af FROMNogle databaseprodukter, herunder SQL Server, tillader brugen af SELECT uden nogen FROM bisætning. Du vil nok kun gøre brug af dette meget sjælgdent, men det skal dog nævnes.
Du kan f.eks. anvende SQL Server som en simpel regnemaskine. Hvis du f.eks. smider dette i munden på SQL Server:
SELECT 2+4, bliver resultatet:
-----------
6
(1 row(s) affected)
Hvis du skal udregne din årlige fede programmørhyre så drop lommeregneren og brug SQL Server:
SELECT 12*70000 giver:
-----------
840000
(1 row(s) affected)
De fleste database administratorer vil nok blive ret så knotten hvis alle firmaets ansatte sad og belemrede hans elskede SQL Server med sådanne regnestykker, men det er muligt. MySQL understøtter også disse "features".
Hvis du er i tvivl om dags dato kan SQL Server også her hjælpe:
SELECT GETDATE() giver:
-----------------------
2008-01-07 15:50:40.250
(1 row(s) affected)
Det er lidet imponerende, og nok ikke noget du vil bruge så tit, men nu kender du til det.
Du kan regne med SELECT: Hvordan du bruger udtryk i SELECTDu har netop set hvordan du kan få SQL Server (og MySQL) til at udregne simple regnestykker for dig ved at bruge regneudtryk i en SELECT uden en FROM bisætning. Brugen af sådanne regneudtryk bliver dog først interesserant når de bruges på noget data fra en tabel.
Hvis f.eks. vi ønsker at se en liste over vores skibe samt deres hastighed i både knob og km/t, kan vi bruge et udtryk i SELECT sætningen der omregner hastigheden i knob, som står i tabellens
Speed-kolonne, til hastighed i km/t. Fra afsnittet om
Warships databasen skrev jeg at en knob er 1,852 km/t og det kan vi bruge som omregningsfaktor. Følgende SELECT producerer det ønskede resultat:
SELECT Name, Speed AS 'Speed in knots', Speed * 1.852 AS 'Speed in km/h'
FROM Ships
Bemærk at jeg bruger kolonne alias så jeg kan forsyne resultatmængdens kolonner med nogle meningsfulde overskrifter. Resultatet bliver:
Name Speed in knots Speed in km/h
-------------------------------------------------- ---------------------- ----------------------
HMS Abyssinia (1870) 9,59 17,76068
HMS Africa (1905) 18 33,336
HMS Agamemnon (1879) 13 24,076
HMS Agincourt (1913) 22,4 41,4848
HMS Ajax (1880) 13 24,076
HMS Ajax (1912) 21,5 39,818
Japanese battleship Aki 20 37,04
HMS Albemarle (1901) 19 35,188
HMS Albion (1898) 18 33,336
HMS Alexandra (1875) 15,09 27,94668
Chilean battleship Almirante Latorre 22,75 42,133
HMS Anson (1886) 15,7 29,0764
HMS Audacious (1869) 13,5 25,002
HMAS Australia (1911) 25 46,3
(14 row(s) affected)
(Interessant skibsfakta: HMS betyder His Majesty's Ship eller Her Majesty's Ship - eller Submarine - og buges på den engelske flådes skibe. For de australske skibes vedkommende bruges Her Majesty's Australian Ship.)Hvis du absolut skal være unik: Brugen af DISTINCTSom vi så under afsnittet om den kraftfulde asterisk (*), kan DISTINCT bruges til fjerne gentagne rækker fra resultatmængden. Lad os kigge lidt nærmere på dette nøgleord.
DISTINCT virker på hele rækken, og ikke blot på det udtryk som det står foran. Tage følgende forespørgsel:
SELECT Speed, Draught FROM Ships:
Speed Draught
---------------------- ----------------------
9,59 4,45
18 8,2
13 7,16
22,4 8,2
13 7,16
21,5 8,4
20 8,4
19 7,5
18 7,9
15,09 8
22,75 10
15,7 8,48
13,5 7
25 9,1
(14 row(s) affected)
Bemærk her at rækken (13, 7.16) optræder to gange. Bemærk også at værdien 18 optræder to gange i den første kolonne, men at de komplette rækker er forskellige (18, 8.2) og (18, 7.9). Som sagt tager DISTINCT hele rækken i betragning når den fjerner nogle rækker, så den ene af (13, 7.16) vil blive fjernet, mens at de to rækker med 18 i første kolonne vil optræde i resultatmængden da der er tale om to forskellige rækker.
SELECT DISTINCT Speed, Draught FROM Ships giver:
Speed Draught
---------------------- ----------------------
9,59 4,45
13 7,16
13,5 7
15,09 8
15,7 8,48
18 7,9
18 8,2
19 7,5
20 8,4
21,5 8,4
22,4 8,2
22,75 10
25 9,1
(13 row(s) affected)
Med andre ord: DISTINCT virker på mængden (Speed, Draught), dvs. hele rækken, og ikke blot på (Speed). DISTINCT kan derfor kun optræde én gang i en SELECT sætning hvorfor sætningen
SELECT DISTINCT Speed, DISTINCT Draught FROM Ships er ulovlig.
Hvis din SELECT indeholder mindst én nøgle for hver tabel nævnt i FROM, er DISTINCT overflødig (men ikke ulovlig). Dette er logisk da inklusionen af en nøglekolonne netop sørger for at resultatet er distinkt, da en tabel med nøgler aldrig har gentagne rækker.
Derudover, hvis resultatet kun indeholder én (eller ingen) rækker har DISTINCT selvsagt ingen virkning.
ALL er unødvendigt
Vi har lige snakket om DISTINCT. DISTINCTs modsætning er ALL som kan optræde på samme måde i en SELECT som DISTINCT. ALL har klart nok den modsatte virkning til DISTINCT. ALL er med andre ord ikke nødvendig at angive eksplicit.
De to følgende SELECT sætninger er altså ens:
SELECT Speed, Draught
FROM Ships
SELECT ALL Speed, Draught
FROM Ships
Alt er ikke som det synes: Hvornår to rækker er ensHvis du har fulgt med, ikke blot i denne artikel, men i hele din SQL læretid, så har du måske undret dig over noget i forbindelse med DISTINCT.
Dengang du lærte om WHERE bisætningen (enten fra en anden artikel, bog, forelæsning etc.), lærte du helt sikkert om NULL. NULL er ikke en værdi som ande kolonneværdier såsom 4, 'Peter Larsen', 0.05 osv. NULL er en angivelse af fraværet af data. NULL er ikke en værdi, og kan derfor ikke sammenlignes med andre værder. NULL kan heller ikke sammenlignes med andre NULLs.
Tag for eksempel følgnede: NULL = 5. Dette kunne have stået i en WHERE bisætning:
SELECT EnEllerAndenKolonne
FROM EnEllerAndenTabel
WHERE NULL = 5;Udover at være fjollet, er denne SELECT udefineret da NULL = 5 er udefineret. Du kan heller ikke sammenligne NULL med en kolonne, hvorfor
SELECT EnEllerAndenKolonne
FROM EnEllerAndenTabel
WHERE NULL = EnEllerAndenKolonne;også er udefineret.
NULL er sig selv, kun sig selv og ikke andet. NULL kan derfor ikke sammenlignes med en kolonne eller anden værdi.
Såfremt man ønsker at undersøge for eksistensen af NULL skal konstruktet IS NULL anvendes:
SELECT EnEllerAndenKolonne
FROM EnEllerAndenTabel
WHERE EnEllerAndenKolonne IS NULL;Dette burde betyder et problem for DISTINCT. DISTINCT fjerne gentagne rækker og gør brug af sammenligninger i processen. Men hvad med rækker der indheolder NULLs?
Tak f.eks. følgende rækker.
('Peter', NULL, 45);
('Peter', NULL, 45);
('Petersen', NULL, 47);Af disse tre rækker er de to øverste ens uanset forekomsten af NULL. Men DISTINCT sammenligner jo netop rækkerne for at fjerne gentagne rækker. Og som netop forklaret kan man ikke sammenligne NULLs (og hvis man prøver får man et udefineret resultat)!
Så hvordan passer alt dette sammen? Den nemmeste måde at anskue det på (ideen kommer oprindeligt fra Rick van der Lans, se Referencer nedenfor), er at vi kan foretage både horisontale og vertikale sammenligninger.
Når vi bruger betingelser, f.eks. i en WHERE bisætning, foretages en horisontal sammenliging. I en sådanne sammenligning er NULL ikke lige med andre NULLs, og en sammenligning giver et udefineret resultat.
Sammenliginngen af de to rækker:
('Peter', NULL) = ('Peter', NULL)giver et udefineret resultat. Bemærk at jeg har skrevet rækkerne på samme række, og her er altså tale om en horisontal sammenligning. Som nævnt er NULL her ikke sammenlignelig med andre NULLs. Resultatet er ikke en gang falsk, men altså udefineret da en sammenligning ikke er lovlig.
Horisontale sammenligninger er det vi finder i WHERE bisætninger.
Når DISTINCT udfører sit magi, er der tale om vertikale sammenligninger. Så en sammenligning af følgende to rækker:
('Peter', NULL, 45);
('Peter', NULL, 45);returnerer sand, selvom vi her har NULLs; og brugen af DISTINCT vil fjerne den ene af rækkerne. Med andre ord er NULL = NULL i en vertikal sammenligning! Læg mærker til at rækkerne her står neden under hindanden - dvs. i en vertikal opsætning.
Jeg indrømmer at dette med horisontale og vertikale sammenligninger er grænsende til det teoretiske, men det er en helt uformel betragning som kan hjælpe til at forstå hvordan en SQL maskine (
eng.: SQL engine) virker.
Hvad synes du om denne artikel? Giv din mening til kende ved at stemme via pilene til venstre og/eller lægge en kommentar herunder.
Del også gerne artiklen med dine Facebook venner:
Kommentarer (5)
En meget god artikel. Jeg synes der er mange gode ting, som man måske ikke lige er faldet over i andre artikler.
Du skriver at følgende er ulovlig: select name, sum(speed)
Både ja og nej, den kan ikke eksekvere, der mangler en grupperings funktion. Så for at den skal virke skal den hedde select name, "sum(speed) group by name". Samtidig synes jeg starten er lidt malplaceret, du vil skrive om aggregeringsfunktioner, men starter ud med at have 2 sider om alt andet end aggregeringsfunktioner. Du er hurtigt omkring joins, men gør det ikke færdigt...
Det du dækker omkring aggregeringsfunktioner er godt og velskrevet
Hej Brian,
Ang. SELECT name, sum(speed) værende ulovlig så tager jeg selvsagt ikke GROUP BY i betragtning i artiklen overhovedet, og det er med vilje da det er et stort emne for sig selv. Jeg kunne måske havde nævnt det forbehold mere eksplicit.
Meningen var at artiklen udelukkende skulle omhandle SELECT og mængdefunktioner, hverken GROUP BY eller FROM (og altså ej heller JOINs); artiklen var ikke ment til en nybegynder hvorfor jeg antog det rimeligt at læseren havde noget SQL viden i forvejen, og derfor kunne betragte emnerne i isolation.
Jeg medgiver dog at jeg muligvis kunne have gjort disse forbehold mere eksplicit. Du tydeliggør i hvert fald at jeg har fejlet hvad det angår.
Tak for dine kommentarer
Hej Jacob,
en virkelig superfed artikel som der simpelthen har været en fornøjelse at læse.
SQL er virkelig et fedt sprog med ret så mange muligheder. :-)
Meget nyttig artikel, men mest for de brugere som har en lille smule forstand på MySQL vil jeg påskønne.
- Men jeg synes stadig artiklen er til en 5'er!
Og som Martin Th. Sonne siger, det var virkelig en fornøjelse at læse artiklen!
Du skal være
logget ind for at skrive en kommentar.