SELECT i detaljer samt opsamlingsfunktioner
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!