12
Tags:
databaser
Skrevet af
Bruger #4522
@ 06.02.2008
Introduktion
Denne artikel giver en udførlig beskrivelse af SQL sætningen SELECT. Artiklen forudsætter et kendskab til SQL på begynderniveau og bygger videre på det for at give læseren en god og grundig forståelse for SELECT. Kun selve SELECT berøres, så jeg kommer i denne artikel ikke ind på WHERE, FROM eller andre "bisætninger" der ofte anvendes sammen med SELECT.
Artiklen fokuserer også meget på brugen af det jeg på dansk kalder for opsamlingsfunktioner - det engelske udtryk er
aggregation function; disse funktioner kan være utrolig nyttige, men mange begyndere har det lidt svært med dem. Et andet udtryk for dem er mængdefunktioner (ikke at forveksle med mængdeoperationer som der også findes nogle af i SQL).
Et eksempel på en database
For ordentligt at illustrere SELECT er det nødvendig med en database som jeg kan lave nogle forespørgelser mod.
Jeg har til dette formål selv konstrueret en database om krigsskibe (det giver mig forhåbentlig mulighed for på et tidspunkt at kunne bruge udtrykket "splitte mine bramsejl"). Det er ikke en "seriøs" database, men blot en jeg konstruerede til denne artikel ud fra data fra wikipedia. Et af skibene i den lille base er
HMS Africa fra 1905:
Nå, lad os få bygget basen.
Først laver vi selve databasen. Derefter laver jeg tre tabeller:
Ships,
Engines og
Builders.
Ships indeholder de enkelte skibe,
Engines indeholder de motorer som skibene bruger, og den sidste tabel,
Builders, indeholder de skibsværfter som byggede de enkelte skibe.
Følgende SQL Server script konstruerer både databasen og de tre tabeller (det kan meget nemt modificeres til MySQL).
CREATE DATABASE Warships
GO
USE [Warships]
GO
/****** Object: Table [dbo].[Builders] Script Date: 01/04/2008 19:02:44 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Builders](
[BuilderId] [smallint] NOT NULL,
[Name] [char](128) NOT NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: Index [primary_key] Script Date: 01/04/2008 19:31:10 ******/
CREATE UNIQUE NONCLUSTERED INDEX [primary_key] ON [dbo].[Builders]
(
[BuilderId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Engines] Script Date: 01/04/2008 19:03:13 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Engines](
[EngineId] [smallint] NOT NULL,
[Name] [char](128) NOT NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: Index [primary_key] Script Date: 01/04/2008 19:32:07 ******/
CREATE UNIQUE NONCLUSTERED INDEX [primary_key] ON [dbo].[Engines]
(
[EngineId] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
/****** Object: Table [dbo].[Ships] Script Date: 01/04/2008 19:03:24 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Ships](
[Name] [char](128) NOT NULL,
[Builder] [smallint] NOT NULL,
[LaunchDate] [datetime] NOT NULL,
[Length] [tinyint] NOT NULL,
[Engine] [smallint] NOT NULL,
[Speed] [float] NOT NULL,
[Draught] [float] NOT NULL
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
/****** Object: Index [primary_key] Script Date: 01/04/2008 19:32:33 ******/
CREATE UNIQUE NONCLUSTERED INDEX [primary_key] ON [dbo].[Ships]
(
[Name] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
Ok. Nu er vi klar til at indsætte noget data i vores tabeller. Vi starter med nogle af værfterne. Følgende script indsætter dem i vores database:
INSERT INTO Builders VALUES (0, 'Dudgeon, Poplar, London');
INSERT INTO Builders VALUES (1, 'Chatham Dockyard');
INSERT INTO Builders VALUES (2, 'Armstrongs, Newcastle upon Tyne');
INSERT INTO Builders VALUES (3, 'Pembroke Dockyard');
INSERT INTO Builders VALUES (4, 'Kure Naval Yards, Japan');
INSERT INTO Builders VALUES (5, 'Thames Ironworks and Shipbuilding Co. Ltd');
INSERT INTO Builders VALUES (6, 'Armstrong-Whitworth, Elswick');
INSERT INTO Builders VALUES (7, 'Napier shipyards');
INSERT INTO Builders VALUES (8, 'John Brown and Company');
Hvis vi nu laver en SELECT * på
Builders får vi (jeg kommer ind på "SELECT * ..." i detaljer nedenfor):
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
Ok, det var én tabel. Fyldt med data. Nu er det så tid til at indsætte data i
Engines tabellen. Det sørger følgende script for:
INSERT INTO Engines VALUES (0, 'Two-shaft Dudgeon, I.H.P.= 1,200');
INSERT INTO Engines VALUES (1, '4 cylinder compound expansion stream engines, 2 screws, 18,000 hp (13 MW)');
INSERT INTO Engines VALUES (2, 'Two-shaft Penn inverted compound Penn engines');
INSERT INTO Engines VALUES (3, 'Parsons 4-shaft steam turbines');
INSERT INTO Engines VALUES (4, '2-shaft Curtis Turbine');
INSERT INTO Engines VALUES (5, 'Water tube boilers, 2 × vertical triple expansion engines, 2 shafts, 18,000 ihp');
INSERT INTO Engines VALUES (6, '2-shaft Humphreys vertical inverted compound');
INSERT INTO Engines VALUES (7, '21 Yarrow boilers. Low pressure Parsons and High pressure Brown-Curtis turbines');
INSERT INTO Engines VALUES (8, 'Two Shaft Reciprocating Vertical Triple Expansion (VTE) Engines');
INSERT INTO Engines VALUES (9, 'Coal fired reciprocating steam engine');
En SELECT * på den nybefolkede
Engines tabel giver os:
EngineId Name
-------- -------------------------------------------------------------------------------
0 Two-shaft Dudgeon, I.H.P.= 1,200
1 4 cylinder compound expansion stream engines, 2 screws, 18,000 hp (13 MW)
2 Two-shaft Penn inverted compound Penn engines
3 Parsons 4-shaft steam turbines
4 2-shaft Curtis Turbine
5 Water tube boilers, 2 × vertical triple expansion engines, 2 shafts, 18,000 ihp
6 2-shaft Humphreys vertical inverted compound
7 21 Yarrow boilers. Low pressure Parsons and High pressure Brown-Curtis turbines
8 Two Shaft Reciprocating Vertical Triple Expansion (VTE) Engines
9 Coal fired reciprocating steam engine
Så er det tid til at indsætte data i
Ships-tabellen. Endelig! Det sørger følgende script for:
INSERT INTO Ships VALUES ('HMS Abyssinia (1870)', 0, 1870-02-19, 68.58, 0, 9.59, 4.45);
INSERT INTO Ships VALUES ('HMS Africa (1905)', 1, 1905-05-20, 138, 1, 18, 8.2);
INSERT INTO Ships VALUES ('HMS Agamemnon (1879)', 1, 1879-10-17, 91.67, 2, 13, 7.16);
INSERT INTO Ships VALUES ('HMS Agincourt (1913)', 2, 1913-01-22, 205, 3, 22.4, 8.2);
INSERT INTO Ships VALUES ('HMS Ajax (1880)', 3, 1880-03-10, 91.67, 2, 13, 7.16);
INSERT INTO Ships VALUES ('HMS Ajax (1912)', 9, 1912-03-21, 182.3, 3, 21.5, 8.4);
INSERT INTO Ships VALUES ('Japanese battleship Aki', 4, 1907-04-14, 146.9, 4, 20, 8.4);
INSERT INTO Ships VALUES ('HMS Albemarle (1901)', 1, 1901-03-05, 143, 5, 19, 7.5);
INSERT INTO Ships VALUES ('HMS Albion (1898)', 5, 1898-06-21, 131.4, 5, 18, 7.9);
INSERT INTO Ships VALUES ('HMS Alexandra (1875)', 1, 1875-04-07, 99.06, 6, 15.09, 8);
INSERT INTO Ships VALUES ('Chilean battleship Almirante Latorre', 6, 1913-11-27, 190.5, 7, 22.75, 10);
INSERT INTO Ships VALUES ('HMS Anson (1886)', 3, 1886-02-17, 100.58, 6, 15.7, 8.48);
INSERT INTO Ships VALUES ('HMS Audacious (1869)', 7, 1869-02-27, 86, 9, 13.5, 7);
INSERT INTO Ships VALUES ('HMAS Australia (1911)', 8, 1911-10-25, 179.8, 3, 25, 9.1);
En SELECT * fra tabellen
Ships giver os følgende resultatmængde:
Name Builder LaunchDate Length Engine Speed Draught
------------------------- ------- ----------------------- ------ ------ ---------------------- -------
HMS Abyssinia (1870) 0 1905-01-24 00:00:00.000 68 0 9,59 4,45
HMS Africa (1905) 1 1905-02-24 00:00:00.000 138 1 18 8,2
HMS Agamemnon (1879) 1 1905-01-27 00:00:00.000 91 2 13 7,16
HMS Agincourt (1913) 2 1905-03-06 00:00:00.000 205 3 22,4 8,2
HMS Ajax (1880) 3 1905-02-11 00:00:00.000 91 2 13 7,16
HMS Ajax (1912) 9 1905-03-04 00:00:00.000 182 3 21,5 8,4
Japanese battleship Aki 4 1905-03-05 00:00:00.000 146 4 20 8,4
HMS Albemarle (1901) 1 1905-03-09 00:00:00.000 143 5 19 7,5
HMS Albion (1898) 5 1905-02-15 00:00:00.000 131 5 18 7,9
HMS Alexandra (1875) 1 1905-02-08 00:00:00.000 99 6 15,09 8
Chilean battleship... 6 1905-02-19 00:00:00.000 190 7 22,75 10
HMS Anson (1886) 3 1905-02-11 00:00:00.000 100 6 15,7 8,48
HMS Audacious (1869) 7 1905-01-15 00:00:00.000 86 9 13,5 7
HMAS Australia (1911) 8 1905-02-20 00:00:00.000 179 3 25 9,1
Rigtige søfolk bruger ikke udtryk som km/t, så de siger i stedet knob hvorfor hastighedskolonnen (altså
Speed) holder værdier i knob. 1 knob er 1 sømil/time, og 1 sømil = 1,852 km. Så 1 knob er altså næsten 2 km/t.
Draught, som på dansk heder dybgang, angiver anfstanden fra skrogets bund til vandlinjen der hvor skibet ligger lavest. Mange skibe har en måleskala på boven hvor det netop kan ses:
Både dybgang og længde måles i basen i meter.
Det er jo ikke meningen at dette skal være en artikel om skibe, men om SQL, så lad os komme i gang med at kigge på SELECT!
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.