ROC ter AA T&T Team ICT. Cees Loomans [DATABASE SQL] Een aanzet tot de standaard vraagtaal SQL.

Vergelijkbare documenten
Query SQL Boekje. Fredrik Hamer

SQL is opgebouwd rond een basisinstructie waaraan één of meerdere componenten worden toegevoegd.

SQL Aantekeningen 3. Maarten de Rijke 22 mei 2003

12. Meer dan één tabel gebruiken en sub-queries

Informatie & Databases

[TOETS SQL INLEIDING]

1. Inleiding Inleiding SQL Inleiding Database, databaseserver en databasetaal Het relationele model...

DBMS. DataBase Management System. Op dit moment gebruiken bijna alle DBMS'en het relationele model. Deze worden RDBMS'en genoemd.

DATAMODEL SQL. Middelbare School. Versie 1.0 Datum 30 oktober 2010 Auteur Mark Nuyens, studentnummer: Groep TDI 1

Structured Query Language (SQL)

Databases en SQL Foundation (DBSQLF.NL)

DBMS SQL. Relationele databases. Sleutels. DataBase Management System. Inleiding relationele databases. bestaan uit tabellen.

Hoofdstuk: 1 Principes van databases

SQL manipulatietaal. We kunnen er data mee toevoegen, wijzigen en verwijderen uit een database.

SQL datadefinitietaal

SQL STATEMENTS. Deze kolom kan grote stukken tekst aan en is bedoeld om tekst erin de plaatsen. Geheel getal, bijvoorbeeld 8, 63, 835 NUMERIC

Toon TITEL, JAAR en PLATVORM van GAMES die voor het jaar 2000 uitkwamen op Nintendo 64

Databases - Inleiding

hoofdstuk 9 referentiële integriteit waarborgen overige constraints 9.1 Referentiële integriteit relationele databases 9.1

Les S-01: De basisbeginselen van SQL

Inleiding Databases en Data Base Management Systems Tabellen Wat is SQL?... 5

Systeemontwikkeling, Hoofdstuk 4, Tabellen maken in MS Access 2010

SQL.

Introductie (relationele) databases

Structured Query Language

6. Het maken van een database

Les 2 Eenvoudige queries

Bijlage Inlezen nieuwe tarieven per verzekeraar

Les S-01: De basisbeginselen van SQL

Startgids 061 Nieuw product aanmaken en wijzigen

2.4.4 LibreOffice Werkblad Mac

2.2 Een tabel ontwerpen

Data Manipulation Language

EXIN Databases en SQL Foundation

databases & SQL - antwoorden

Van CaseTalk naar een database in SQLite studio

9 H. Flits Zwanenveld Nijmegen Jeugd1 10 L. Willemsen Kasteel 4 Wychen Jeugd1 12 M.E.P. Graag Broerdijk 234 Nijmegen Heren 12/8/89 19/8/89 36

Sparse columns in SQL server 2008

Miniles gegevensbanken bevragen met SQL

SQL opgaven. Relationele model: Opgaven:

SQL: query taal met. woorden. ISO SQL: Structured Query Language. de SQL basis query structuur. voorbeeld: doel: intuitieve query taal

SQL & Relationele datamodellen in interactieve media

ISO SQL: Structured Query Language

Offective > CRM > Vragenlijst

8. De invoer van gegevens

Les 11 : Basis SQL (deel2).

DATABASEBEHEER IN EXCEL

Rekenen aan wortels Werkblad =

Vragen hoofdstuk 1: Resultaat

Inhoud. Voorwoord Belangrijkste kenmerken van dit boek De opzet van dit boek Over de auteur Woord van dank

Databank - Basis 1. Inhoud. Computervaardigheden en Programmatie. Hoofdstuk 4 Databank - Basis. Terminologie. Navigeren door een Venster

In deze appendix wordt bekeken wat er moet gebeuren voordat

Relationele databases

Les S-02: Meer geavanceerde SQL-instructies

gravita PSUR-C conversie en import van relaties in PSU Relatiebeheer Algemeen

SQL / Systeemanalyse

= > >= < <= BETWEEN IS NULL IS NOT NULL

Redundancy Normaalvormen

Zelftest SQL Workshop

De teamleider is een coördinator van de lotenverkoop binnen uw vereniging, hij of zij heeft een aantal lotenverkopers onder zich.

Koppeling met een database

Handleiding ChainWise Data import Module

Gebruikers Handleiding

11. Het selecteren van gegevens deel II

4 Tabellen maken in MS Access In dit hoofdstuk starten we met de bouw van ons informatiesysteem met de belangrijkste bouwstenen: de tabellen.

COMPUTERWOORDEN.NL SQL - basis

W I N D E X C C. ReleaseNotes 1.12

voorbeeldexamen I-Tracks Databases and SQL Foundation Voorbeeldexamen DBSQLF Uitgave juni 2006

Relationele database. Het relationele model

25 Excel tips. 25 Handige Excel tips die tijd besparen en fouten voorkomen. Ir. Fred Hirdes. Excel-leren.nl.

7. Het selecteren van gegevens

U ziet de progressie van de download aan de groene blokjes in het balkje helemaal onder aan de pagina.

1 DATABASE MANAGEMENT

Zelftest SQL Workshop

Elfde-Liniestraat Hasselt Schooljaar TINFO POKER GAME Oracle Scripts

Handleiding Data import module

Handleiding gebruik Basispoort XML Maker

Office LibreOffice Werkblad gebruiken

Spreadsheets (Excel 2003)

Rekenen met de GRM. 1 van 1. Inleiding: algemene zaken. donkerder. lichter

EMBEDDED SQL. Inleiding. Queries en update-opdrachten. Embedden en hostvariabelen

1. * Database worden vaak gebruikt in Client-Server architectuur.

Uw eigen vragen maakt u eenmaal aan en kunt u gebruiken bij elke inspectie.

Excel tips. Handleiding van Helpmij.nl. Auteur: CorVerm

1. Geef code en omschrijving van alle cursussen die precies vier dagen duren.

Spiekboekje Excel Query SQL

En hoe gaan ze dit allemaal terugvinden?

2 Specificatie In deze tabel staat voor welk crebotraject de leereenheid is gemaakt Crebotraject code: 95311

Informatica toets vwo 6 Databases

Gebruikershandleiding

Correctievoorschrift HAVO Informatica. Tijdvak 1 Woensdag 24 mei uur. College-examen schriftelijk.

Workshop 3x. Normaliseren. Normaliseren. Hiëarchische database ODBMS. Relationele database. Workshop 14 oktober A. Snippe ICT Lyceum 1

De inrichting van Orbak Compact Stap-voor-Stap

VBA voor Doe het Zelvers deel 20

Het omzetten van een ER-diagram naar SQL

Computervaardigheden. Universiteit Antwerpen. Computervaardigheden en Programmatie. Grafieken en Rapporten 1. Inhoud. Anatomie van een databank

Database Structuur via menus

Data Definition Language

Data Manipulatie. Query Talen. / Informatica

6.8 Lijsten: oefeningen

Transcriptie:

2010 ROC ter AA T&T Team ICT Cees Loomans [DATABASE SQL] Een aanzet tot de standaard vraagtaal SQL.

Hoofdstuk: Inleiding SQL Inhoud Inleiding SQL... 5 Het opzetten van de database (DDL)... 6 De CREATE TABLE opdracht... 6 De ALTER TABLE opdracht... 7 De DROP TABLE opdracht... 8 Integriteitbewaking met de UNIQUE INDEX... 9 Indexeren... 9 VRAGEN EN OPDRACHTEN... 11 VRAGEN... 11 OPDRACHTEN... 11 Het werken met Gegevens (DML)... 12 Het vullen van de tabel met behulp van INSERT... 12 VRAGEN EN OPDRACHTEN... 14 VRAGEN... 14 OPDRACHTEN... 14 Fundamentele opdrachten met SELECT... 15 VRAGEN... 20 OPDRACHTEN... 20 DISTINCT... 21 OPDRACHTEN... 22 Rekenen met kolominhouden... 23 OPDRACHTEN... 24 Operatoren in het WHERE gedeelte.... 25 OPDRACHTEN... 26 Het combineren van operatoren met AND en OR... 27 In SQL kunnen we dan gebruik maken van AND en OR.... 27 OPDRACHTEN... 28 Het gebruik van NOT... 29 VRAGEN EN OPDRACHTEN... 30 VRAGEN... 30 OPDRACHTEN... 30 BETWEEN... 31 OPDRACHTEN... 31 IN... 32 OPDRACHTEN... 32 2

Hoofdstuk: Inleiding SQL LIKE... 33 OPDRACHTEN... 33 NULL... 34 OPDRACHTEN... 34 ORDER BY... 35 OPDRACHTEN... 36 GROUP BY, HAVING, COUNT, AVG, SUM, MAX en MIN... 37 Count... 38 AVG... 38 SUM... 38 MAX en MIN... 39 HAVING... 40 OPDRACHTEN... 41 HET RAADPLEGEN VAN MEERDERE TABELLEN... 42 De verschillende mogelijkheden... 42 De JOIN... 44 OPDRACHTEN... 46 De OUTERJOIN... 47 De AUTOJOIN... 47 OPDRACHTEN... 49 De NON-EQUIJOIN... 50 De SUBQUERY... 50 OPDRACHTEN... 52 ANY en ALL... 53 OPDRACHTEN... 53 De gesynchroniseerde SUBQUERY... 55 De EXISTS operator... 56 OPDRACHTEN... 56 UNION, MINUS en INTERSECT... 57 OPDRACHTEN... 58 VEEL VOORKOMENDE PROBLEMEN MET DE SELECT OPDRACHT... 59 De denkwijze... 59 Nog nooit...... 59 OPDRACHTEN... 60 Alleen maar...... 60 OPDRACHTEN... 61 3

Hoofdstuk: Inleiding SQL De (dubbele) ontkenning... 62 OPDRACHTEN... 63 Alle...... 64 OPDRACHTEN... 66 DE DATA DICTIONARY... 67 Het principe van de data dictionary... 67 De data dictionary als bewaker van de database.... 67 VIEWS... 70 Het principe van de VIEW... 70 Het maken van een VIEW... 70 OPDRACHTEN... 72 Het verwijderen van een VIEW... 72 GEGEVENS WIJZIGEN... 73 De verschillende mogelijkheden... 73 INSERT... 73 OPDRACHTEN... 73 UPDATE... 74 OPDRACHTEN... 74 DELETE... 75 OPDRACHTEN... 75 GEGEVENS BEVEILIGEN... 76 Vormen van gegevensbeveiliging... 76 Bescherming tegen verlies... 76 Autorisatie van gebruik... 76 VRAGEN... 79 OPDRACHTEN... 79 4

Hoofdstuk: Inleiding SQL Inleiding SQL De bedoeling van de cursus is om te leren SQL vragen te stellen aan een relationele database. Door het zelfstandig doornemen van de cursus en daarbij de vragen en opdrachten te maken leert de student hoe hij of zij een database kan bevragen. We maken daarbij gebruik van de database MYSQL en een voorbeeld database. Vraag aan je docent hoe je de database benaderd. SQL (in Amerikaanse termen uit te spreken als 'sequel') is sedert de eerste publicaties van Codd in 1971 en Boyce en Chamberlin in 1973 in sneltempo uitgegroeid tot de standaardtaal voor vierde generatie relationele databases. Juist omdat SQL zo nauw aansluit bij de reeds bekende relationele algebra en calculus en vrij eenvoudige instructies kent voor alle aspecten van het opzetten en beheren van een database is de populariteit van SQL snel toegenomen en nog steeds groeiende. Een waarschuwing is hierbij meteen van belang: SQL is inderdaad vrij snel aan te leren en lijkt daardoor de oplossing voor vele problemen. Niets is minder waar. In het nu volgende zal ik proberen om aan de hand van een voorbeeld database duidelijk te maken hoe eenvoudig SQL aan te leren is. Aan de andere kant hoop ik ook duidelijk te maken dat deze eenvoud meteen het grootste gevaar is omdat men al snel denkt de juiste oplossing gevonden te hebben, terwijl achteraf soms blijkt dat dit nu juist niet het geval was! SQL wordt veel gebruikt in combinatie met dynamisch gegenereerde webpagina s op het internet. Hierbij worden allerlei gegevens, artikelen en dergelijke in de database opgeslagen en indien nodig opgeroepen en getoond. Om later zelf dynamische pagina s te kunnen maken is kennis van SQL noodzakelijk. 5

Hoofdstuk: Het opzetten van de database (DDL) Het opzetten van de database (DDL) De CREATE TABLE opdracht Voor het opzetten van tabellen kent SQL een vrij eenvoudig commando namelijk het CREATE TABLE commando dat er als volgt uit ziet: CREATE TABLE <tabelnaam> ( <kolomnaam> <kolomtype> {(<lengte>)} {NOT NULL}, <kolomnaam2> <kolomtype> {(<lengte>)}{not NULL}, ) De notatiewijze van de commando's in dit boek moeten we als volgt lezen: - HOOFDLETTERS betekenen een letterlijk in te tikken woord - kleine letters tussen < > wil zeggen een zelf in te vullen woord - tekst tussen { } is optioneel - een serie... geeft aan dat het gehele voorgaande stuk meerdere malen herhaald mag worden - alle andere leestekens zoals, en ( ) moeten op de aangegeven plaatsen gebruikt worden. Gemakshalve gaan we hieruit van een nogal "kale" CREATE TABLE opdracht. In een latere cursus zal nog worden stilgestaan bij de uitbreidingen op deze opdracht en de rol van de data dictionaire. Bij de bepaling van het kolomtype kunnen we kiezen uit verschillende mogelijkheden. De belangrijkste typen zijn: DECIMAL CHAR DATE LONG voor kolommen met decimale getallen voor alfanumerieke kolommen voor datumkolommen voor alfanumerieke kolommen met zeer veel karakters Bij DECIMAL kunnen we de lengte en eventueel het aantal decimalen aangeven. Bij CHAR moeten we de lengte aangeven. Naast deze standaardtypen komt (afhankelijk, van het RDBMS waar mee gewerkt wordt) nog een groot aantal andere typen (VARCHAR, LONG VARCHAR, DECIMAL, FLOAT, INTEGER, SMALLINT en dergelijke) voor, die echter niet meer dan variaties op deze vier basistypen zijn. We zouden dus de volgende opdracht kunnen maken: CREATE TABLE WERKNEMERS( CODE CHAR(3) NOT NULL, NAAM CHAR(15) NOT NULL, VOORLETTERS CHAR(8), VOORVOEGSEL CHAR(8), ADRES CHAR(20), POSTCODE CHAR(7), WOONPLAATS CHAR(20), TELEFOON CHAR(12), DATUM_IN_D DATE, SAL DECIMAL(8,2), CHEF CHAR(3) ) 6

Hoofdstuk: Het opzetten van de database (DDL) Hierdoor ontstaat er een tabel met een elftal kolommen waarmee we onze werknemers kunnen beschrijven. Een aantal opvallende zaken zullen we eerst bespreken. Bij een tweetal kolommen geven we de kwalificatie NOT NULL, dat wil zeggen dat deze kolom ingevuld moet worden. Het is namelijk niet toegestaan dat een werknemer opgenomen wordt die nog geen CODE heeft (denk aan de integriteit!) en bovendien stellen we vast dat van iedere werknemer in ieder geval zijn of haar NAAM bekend moet zijn. De volledige naam van iedere werknemer hebben we opgesplitst in een kolom NAAM, een kolom VOORLETTERS en een kolom VOORVOEGSEL voornamelijk omdat dit bij het maken van gesorteerde overzichten erg handig kan zijn. We zorgen er dan voor dat bij het alfabetiseren, werknemer 'De Jonge' na 'Jansma' wordt geplaatst in plaats van voor 'Dekker'. Ondanks het feit dat een telefoonnummer uit cijfers bestaat hebben we hier toch gezegd dat een telefoonnummer van het type CHAR is. Dit heeft een tweetal redenen; in de eerste plaats zorgen we er zo voor dat de nullen die vooraan staan bij een telefoonnummer niet weggehaald worden (dus geen 20 in plaats van 020) en bovendien voorkomen we zo dat 020-1234567 de waarde -1234547 toebedeeld krijgt. Bij de kolom datum van indiensttreding (DATUM_IN_D) hebben we alleen het type van de kolom (DATE) en niet de lengte opgegeven omdat de lengte door het RDBMS zelf bepaald is en dus niet apart opgegeven hoeft te worden. De kolom salaris (SAL) is van het type DECIMAL en heeft een lengteaanduiding 8,2. Dat wil zeggen dat deze kolom in totaal 8 posities breed zal worden waarvan twee posities door de decimalen in beslag zullen worden genomen. De kolom CHEF bestaat uit een drietal karakters. Dit is uiteraard niet toevallig evenveel als de kolom CODE omdat in deze kolom eenvoudig weg de CODE van de CHEF van de desbetreffende werknemer wordt ingevuld. In de gegevensanalyse spreekt men dan wel van een CODE in de rol van CHEF, met andere woorden deze CODE speelt, als het ware een beetje toneel en heeft zich vermomd als CHEF. De ALTER TABLE opdracht Eigenlijk zou het CREATE commando voldoende moeten zijn voor het opzetten van een database. Helaas is het zo dat we ten tijde van het opzetten van de database niet echt in de toekomst kunnen kijken. Zo kan het voorkomen dat we ons bij de analyse van de situatie vergist hebben in de mogelijke groei van een bepaalde waarde: (In Amerika zorgde de eerste cheque boven de 9.999,99 dollar die de Social Security Agency moest boeken voor een onkostenpost van tientallen Miljoenen dollars). Om in staat te zijn een wijziging in een tabel aan te brengen zonder omslachtige ex- en import operaties uit te hoeven voeren hebben we de beschikking over het ALTER TABLE commando. Met behulp van de ALTER TABLE opdracht kunnen we verschillende soorten wijzigingen aanbrengen. In deze paragraaf zullen we overigens niet ingaan op de beperkingen en andere regels die door middel van ALTER TABLE in de data dictionaire kunnen worden vastgelegd. Dit wordt nog in later hoofdstuk aan de orde gesteld. 7

Hoofdstuk: Het opzetten van de database (DDL) In de eerste plaats kan het voorkomen dat de lengte of het type van een kolom gewijzigd moet worden. In een aantal RDBMS-en kunnen we hier een optie van de ALTER TABLE opdracht voor gebruiken: ALTER TABLE <tabelnaam> MODIFY (<kolomnaam> <kolomtype> [(<lengte>)] [ NULL NOT NULL ],...) We zouden dus onze tabel aan kunnen passen door op te geven ALTER TABLE WERKNEMERS MODIFY (SAL DECIMAL(9,2)) Na het geven van deze opdracht is in de tabel WERKNEMERS de kolom SAL een positie breder geworden. De toevoeging NOT NULL kan alleen gegeven worden als de desbetreffende kolom in de gehele tabel in iedere rij een waarde heeft. Bovendien mag een bestaande kolom alleen kleiner gemaakt worden of een ander kolomtype gegeven worden als in alle rijen van de tabel deze kolom nog oningevuld (oftewel NULL) is. Een tweede soort wijziging zou kunnen zijn dat er een kolom toegevoegd moet gaan worden. Daarvoor gebruiken we de volgende opdracht: ALTER TABLE <tabelnaam> ADD (<kolomnaam> <kolomtype> [(<lengte>)] [ NULL NOT NULL ],...) We zouden bijvoorbeeld op kunnen geven ALTER TABLE WERKNEMERS ADD (SOFI_NUM CHAR(10)) Dit heeft tot gevolg dat aan de tabel WERKNEMERS een kolom SOFI NUM voor het sociaalfiscaal nummer van de werknemers wordt toegevoegd. Een kolom toevoegen die als NOT NULL gedefinieerd wordt kan alleen als de tabel nog geen enkele rij bevat. De standaard waarde voor de MODIFY optie is NULL, met andere woorden deze kolom mag oningevuld blijven. De DROP TABLE opdracht Voor het weer verwijderen van tabellen is er in SQL een zeer eenvoudige en daardoor ook zeer gevaarlijke opdracht DROP TABLE <tabelnaam> Als we dus op zouden geven DROP TABLE WERKNEMERS zou dit betekenen dat we in één regel een opdracht hebben ingetikt die een gehele tabel zonder meer verwijdert. Voorzichtigheid is hier dus geboden! 8

Hoofdstuk: Het opzetten van de database (DDL) Integriteitbewaking met de UNIQUE INDEX Bij het definiëren van onze tabel zijn we er van uitgegaan dat in elk geval de CODE en de NAAM van een werknemer bekend moeten zijn. Dat is een eerste stap in de richting van een integere database. Een probleem blijft echter dat we er bij sommige RDBMS-en niet voor kunnen zorgen dat in de kolom CODE bij het toevoegen van een rij steeds een unieke code wordt gebruikt. Om er in die gevallen voor te zorgen dat een sleutelkolom alleen unieke waarden kan bevatten, kunnen we een UNIQUE INDEX aanmaken. Dat doen we door op te geven CREATE [UNIQUE] INDEX <indexnaam> ON <tabelnaam>(.<kolomnaam>,...) UNIQUE is dus een optie van de CREATE INDEX opdracht omdat in SQL gebruik gemaakt wordt van indices om het opzoeken van gegevens te versnellen. We zullen het principe van de index verder behandelen in een van de volgende paragrafen. Wat betreft het toekennen van een naam aan een index zijn we geheel vrij. Toch is het verstandig om ook hier enige systematiek in acht te nemen. In de praktijk is. Bijvoorbeeld een verkorte weergave van de tabelnaam en de kolom(men) waarop de index gedefinieerd is uitermate handig. We zouden dus kunnen opgeven CREATE UNIQUE INDEX WERKNM_CODE ON WERKNEMERS(CODE) Hierdoor wordt bij iedere nieuwe rij die toegevoegd wordt aan de tabel WERKNEMERS eerst gecontroleerd of de waarde in de kolom CODE niet al bestaat in de tabel WERKNEMERS. Uit het feit dat we bij het opgeven van de kolomnaam ook meerdere kolomnamen op mogen geven kunnen we al afleiden dat het dus ook mogelijk is om samengestelde sleutels op deze manier uniek te houden. Zo zouden we op kunnen geven CREATE UNIQUE INDEX WRKNM_NM_TEL ON WERKNEMERS(NAAM, TELEFOON) Zodat bij iedere nieuwe rij eerst gecontroleerd wordt of de combinatie van NAAM en TELEFOON niet al bestaat in de tabel. In steeds meer implementaties is het op deze wijze bewaken van de integriteit overgenomen door het opnemen van regels en beperkingen in de data dictionaire. Dit wordt in een alter hoofdstuk aangegeven. Indexeren In de vorige paragraaf hebben we het gebruik van een UNIQUE INDEX aan de orde gesteld. Daarmee konden we er voor zorgen dat er in een bepaalde kolom (of een bepaalde combinatie van kolommen) alleen unieke waarden ingevuld konden worden. Dit is echter niet het enige gevolg van het aanmaken van een (UNIQUE) INDEX. 9

Hoofdstuk: Het opzetten van de database (DDL) Naast het bewaken van de uniciteit zorgt een index ook voor het versnellen van de zoekoperaties. We hebben al opgemerkt dat de UNIQUE INDEX een optie is van het algemene lndex commando. Als we een index aanmaken betekent dit dat we de mogelijkheid creëren om de desbetreffende tabel te benaderen via de index in plaats van het van begin tot einde doorzoeken van de tabel. Er wordt als het ware een index zoals bij een boek aangemaakt waardoor het RDBMS door gebruik te maken van deze index de gezochte rijen veel sneller terug kan vinden. Naast dit voordeel van de tijdwinst bij het opzoeken van rijen kan het aanmaken van indices ook een nadeel hebben. Net als bij de index van een boek zal een wijziging in de tabel tot gevolg hebben dat de index ook bijgewerkt moet worden. Bij het aanbrengen van wijzigingen zal een index dus eerder tot tijdverlies dan tot tijdwinst leiden. Vandaar dat het van belang is om slechts indices aan te maken op kolommen waar vaak op gezocht zal worden en niet op kolommen die sterk aan wijzigingen onderhevig zijn. Bovendien moeten we ons realiseren dat bij vrijwel alle RDBMS-en die met SQL benaderd kunnen worden het onmogelijk is om het RDBMS te dwingen gebruik te maken van de index. Er is een zogenaamde optimizer aanwezig die zelf zal bepalen of het gebruik van de index nuttig is of niet. Als we in de tabel WERKNEMERS dus versneld willen zoeken op grond van bijvoorbeeld de CODE van de werknemers zouden we een index aan kunnen maken met CREATE INDEX WERKN_CODE ON WERKNEMERS(CODE) Het weer verwijderen van een INDEX kunnen we uitvoeren door gebruik te maken van de opdracht DROP INDEX <indexnaam> 1 0

Hoofdstuk: VRAGEN EN OPDRACHTEN Deze opdrachten en vragen kunnen alleen worden uitgeprobeerd op de database indien U deze zelf heeft geïnstalleerd. VRAGEN 1. Waarom zouden we bij het toevoegen van een kolom aan een bestaande tabel niet de NOT NULL kwalificatie mee kunnen geven? 2. Waar zorgt de toevoeging UNIQUE voor bij de volgende index: CREATE UNIQUE INDEX KL_NM_PC ON KLANTEN(NAAM, POSTCODE) 3. Waarom zou het onmogelijk zijn om een bestaande kolom smaller te maken? OPDRACHTEN 1. Geef de opdracht in SQL voor het aanmaken van de tabel waar de gegevens van een student kunnen worden opgeslagen. Zorg ervoor dat in ieder geval de naam, adres, postcode, telefoonnummer, geboortedatum en datum van start van de studie worden opgenomen. (Bepaal zelf het type en de lengte!) 2. Geef de opdracht in SQL om aan de tabel uit opgave 1 een kolom genaamd EXPORTCODE van 4 karakters toe te voegen. 3. Geef de opdracht in SQL om deze EXPORTCODE vervolgens 1 karakter breder te maken. 4. Geef de opdracht in SQL om een tabel KLANTEN aan te maken met daarin opgenomen een alfanumerieke KLANTCODE kolom die altijd ingevuld moet zijn, een alfanumerieke NAAM kolom, een numerieke CRED_LIMIET kolom en een datumkolom genaamd KLANT_SINDS. Bepaal zelf de lengtes. 5. Geef de opdracht In SQL om bij de KLANTEN tabel een index aan te maken op de kolom KLANTCODE. 6. Geef de opdracht in SQL om er met behulp van indexeren voor te zorgen dat de combinatie van de kolommen KLANTCODE en NAAM in iedere rij uniek zal blijven. 1 1

Hoofdstuk: Het werken met Gegevens (DML) Het werken met Gegevens (DML) Het vullen van de tabel met behulp van INSERT Voordat we ook maar iets kunnen doen met een tabel zullen er uiteraard eerst gegevens in moeten staan. We zuilen dat hier doen met de INSERT opdracht. In de praktijk zal het INSERT commando een ondergeschikte rol spelen omdat het zeer vaak veel eenvoudiger is om met behulp van een applicatiegenerator een invoerscherm te maken zodat. de vrij omslachtige wijze van werken bij de INSERT opdracht vermeden kan worden. Met de INSERT opdracht kunnen we één rij tegelijk toevoegen aan een bepaalde tabel INSERT INTO <tabelnaam> [(kolomnaam,...)] VALUES (waarde,...) Zo zouden we in de tabel WERKNEMERS zoals eerder aangemaakt een aantal rijen kunnen inbrengen door gebruik te maken van INSERT INTO WERKNEMERS VALUES ('10', 'BUCK', 'A.P.', 'DE', 'LINDELAAN 2', '1055 PP', 'AMSTERDAM', '020-6768087', '01-JAN- 80', 4567, '99') INSERT INTO WERKNEMERS VALUES ('12', 'BRASSER', 'K.J.', NULL, 'LINDELAAN 18', '1055 PP', 'AMSTERDAM', '020-9874386', '12-OCT-88', 2149, '20') INSERT INTO WERKNEMERS VALUES ('16', 'LIEVENSE', 'I.H.', NULL,'HOOFDSTRAAT 3', '8948 KK', 'LEEUWARDEN', '058-8916534','14-0CT-95', 3148, '30') INSERT INTO WERKNEMERS VALUES ('18', 'VLIET', 'P.', 'VAN','KADE 34', '1056 KM', 'AMSTERDAM', '020-9321072', '28-SEP-85',2638, '20') INSERT INTO WERKNEMERS VALUES ('19', 'UMBGROVE', 'M.M.',NULL, 'TALMALAAN 3', '4351 JG', 'VLISSINGEN', '0118-439816','13-NOV- 87', 2988, '30') INSERT INTO WERKNEMERS VALUES ('20', 'MAAS', 'L.C.', NULL','BOSJESLAAN 3', '3085 HG', 'ROTTERDAM', '010-9859526', '01-FEB-84', 3332, '10') INSERT INTO WERKNEMERS VALUES ('25', 'NEVE', 'G.', 'DE', 'PRINSENGRACHT 20', '1048 GP', 'AMSTERDAM', '020-9865743', '15- MAY-91', 3148, '40') INSERT INTO WERKNEMERS VALUES ('26', 'GEEL', 'J.M.G.', 'VAN','PLEIN 43', '3568 JL', 'UTRECHT', '030-7385195', '01-DEC- 88',1921, '30') INSERT INTO WERKNEMERS VALUES ('28', 'ZEEUW', 'A.J.', 'DE', 'JULlANASTRAAT 6', '5610 JP', 'EINDHOVEN', '040-5932182', '12- AUG-86', 2981, '35') 1 2

Hoofdstuk: Het werken met Gegevens (DML) INSERT INTO WERKNEMERS VALUES ('30', 'MERK', 'J.', NULL, 'EIKENLAAN 12', '3086 KK', 'ROTTERDAM', '010-9734733', '01-MAR- 86',4749, '40') INSERT INTO WERKNEMERS VALUES ('31', 'PAREE', 'G.M.', NULL,'KADE 44:, '1056 KM', 'AMSTERDAM', '020-9753264', '25-MAR-90', 2745, '35') INSERT INTO WERKNEMERS VALUES ('35', 'JONGE', 'L.M.', 'DE','GRACHT 11', '6222 AG', 'MAASTRICHT', '043-3324785', '30- JUN-95', 2118, '40') INSERT INTO WERKNEMERS VALUES ('40', 'WILLEGEN', 'W.M.I,'VAN', 'BEUKENLAAN 12', '3088 LM', 'ROTTERDAM', '010-9865843', '01-MAR- 78', 4988, '99') INSERT INTO WERKNEMERS VALUES ('45', 'JOBSE', 'G.C.', NULL,'SINGEL 128', '3088 JP', ~ ROTTERDAM', '010-9434785', '15- JUL-86',4716,'40') INSERT INTO WERKNEMERS VALUES ('47', 'JANSMA', 'L.M.', NULL,'SINGEL 48', '1038 JG', 'AMSTERDAM', '020-4343210', '14- APR-84', 2144, '35') INSERT INTO WERKNEMERS VALUES ('48', 'BOS', 'P.', NULL, 'BEUKENLAAN 18', '3088 LM', 'ROTTERDAM', '010-9324321, '28-NOV- 87', 2357, '60') INSERT INTO WERKNEMERS VALUES ('50', 'LOOF', 'J.G.', 'DE', 'PLEIN 12', '3568 JL', 'UTRECHT', '030-9678765', '01-AUG-86', 4328, '10') INSERT INTO WERKNEMERS VALUES ('52', 'MAAS', 'M.C.', 'VAN DER', 'HOGEWEG 12', '3078 KL', 'ROTTERDAM', '010-3544567','12-DEC-86', 2849, '60') INSERT INTO WERKNEMERS VALUES ('60', 'VLIET', 'P.C.', 'VAN DER','ORANJEPLEIN 3', '5655 BG', 'EINDHOVEN', '040-9143421', '01-APR-79',3848, '10') INSERT INTO WERKNEMERS VALUES ('99', 'KONING', 'C.', 'DE', 'SINGEL 2', '3088 JK', 'ROTTERDAM', '010-6493214', '01-JAN- 80',5624, NULL) Uit het voorafgaande wordt meteen duidelijk dat dit een nogal omslachtige wijze van het invoeren van rijen is. Ook wordt duidelijk dat we bij de INSERT opdracht een aantal regels in acht moeten nemen. 1 3

Hoofdstuk: Het werken met Gegevens (DML) Waarden die ingevuld moeten worden in een alfanumerieke (CHAR) kolom moeten met enkele aanhalingstekens worden omgeven. Waarden in DATE kolommen moeten in het formaat DD-MON-YY en tussen aanhalingstekens ingevoerd worden. Numerieke (DECIMAL) kolommen moeten zonder aanhalingstekens ingevoerd worden. Indien een bepaalde kolom niet ingevuld hoeft te worden gebruiken we NULL. De kolomnamen hoeven we alleen op te geven als we een volgorde hanteren die afwijkt van de volgorde van de kolommen zoals we die gebruikt hebben bij de CREATE TABLE opdracht of als we bepaalde kolommen niet willen invullen zonder steeds NULL te gebruiken. We zouden bijvoorbeeld alleen de CODE en de NAAM van een werknemer in kunnen voeren door op te geven INSERT INTO WERKNEMERS (CODE,NAAM)VALUES ('80','JANSEN') VRAGEN EN OPDRACHTEN Deze vragen en opdrachten kunnen alleen worden uitgeprobeerd indien U zelf de database heeft geïnstalleerd. VRAGEN 1. Wat zijn de belangrijkste aandachtspunten bij het gebruik van respectievelijk CHAR, NUMBER, DATE en NULL waarden in een INSERT opdracht? OPDRACHTEN 1. Geef de INSERT opdracht voor het invoeren van de volgende rijen in de WERKNEMERS tabel 54 P.KATS BEUKENLAAN 1243057 HG ROTTERDAM geen telefoon, in dienst per 1 april 1989 met een nog onbekend salaris, de chef wordt 30. 63 K.5MULDERS P.C..HOOFTSTRAAT 56 1033 KL AMSTERDAM telefoon 020-6843762, in dienst met ingang van heden, salaris wordt 3567, de chef wordt 40. 2. Geef een zo kort mogelijke lnsert opdracht om de volgende rij aan de tabel toe te voegen 71 K.SCHREURS, telefoon 0118-493218, nadere gegevens nog niet bekend. SPECIAL Om de opdrachten van de volgende hoofdstukken te maken is het nodig dat je de database netjes aanmaakt en invult. Dit kan met de opdrachten die we hierboven geleerd hebben. Echter, het is waarschijnlijk dat er dan veel type fouten gemaakt zullen worden waardoor het een en ander niet bepaald soepel zal verlopen. Er is een bestand gemaakt SQLCURSUS.TXT dat alle opdrachten die nodig zijn netjes bevat. Je kunt dit importeren in je databasemanagement programma. Ga in de documentatie van het RDBM na hoe dat moet en importeer het dan. 1 4

Hoofdstuk: Het werken met Gegevens (DML) Fundamentele opdrachten met SELECT Met behulp van de SELECT opdracht zijn we in staat om een aantal fundamentele handelingen te verrichten. We kunnen namelijk bepaalde kolommen uit de tabel opvragen (PROJECTIE), bepaalde rijen uit de tabel selecteren (SELECTIE) en we kunnen gegevens uit meerdere tabellen gecombineerd opvragen (JOIN). We zullen deze aspecten één voor één behandelen. Bij de PROJECTIE gaat het er om dat we slechts bepaalde kolommen uit de tabel willen zien. In de WERKNEMERS tabel zou dit bijvoor- beeld de NAAM, de WOONPLAATS en het TELEFOONNUMMER van de werknemers kunnen zijn. In een figuur weergegeven ziet PROJECTIE er als volgt uit PROJECTIE In SQL kunnen we een projectie uitvoeren door in een SELECT instructie slechts bepaalde kolommen te noemen,woonplaats, TELEFOON Deze SELECT opdracht geeft meteen aan wat er minimaal voor moet komen in een dergelijke opdracht. In de eerste plaats geven we aan welke kolommen we willen zien (de PROJECTIE dus) en in de tweede plaats geven we na FROM aan uit welke tabel de kolommen moeten komen. Bij de keuze van de kolommen hoeven we geen rekening te houden met de volgorde waarin deze voorkomen in de tabel, iedere volgorde is toegestaan zolang we de namen van de kolommen maar juist opgeven. Als we alle kolommen van een tabel willen zien dan gebruiken we een *. 1 5

Hoofdstuk: Het werken met Gegevens (DML) Dus: SELECT* geeft een overzicht van alle kolommen (en alle rijen) uit de WERKNEMERS tabel. Als we de informatie uit de tabel willen beperken tot bepaalde rijen (een SELECTIE uitvoeren) dan doen we dit door achter het FROM gedeelte een voorwaarde gedeelte aan te geven met behulp van WHERE. We zouden bijvoorbeeld informatie op kunnen vragen uit onze WERKNEMERS tabel door alleen de werknemers uit UTRECHT te selecteren SELECT * WHERE WOONPLAATS = 'UTRECHT' Hierdoor worden alle rijen uit de tabel waarvoor geldt dat in de kolom WOONPLAATS de waarde UTRECHT is ingevuld op ons scherm afgedrukt. Er is dus een SELECTIE uitgevoerd. PROJECTIE Bij het gebruik van een alfanumerieke constante in het WHERE gedeelte moeten we gebruik maken van enkele aanhalingstekens om aan te geven dat het hier om een alfanumerieke constante gaat. Dit geldt ook voor een datum constante. Numerieke constanten kunnen zonder de enkele aanhalingstekens gebruikt worden. Dus WHERE <kolomnaam> = 'CHAR constante' WHERE <kolomnaam> = 'DATE constante' WHERE <kolomnaam> = NUMBER constante Uiteraard zal het heel vaak zo zijn dat we een SELECTIE en een PROJECTIE willen combineren. Bijvoorbeeld als we van alle werknemers uit UTRECHT alleen de NAAM, de WOONPLAATS en het TELEFOONNUMMER willen zien 1 6

Hoofdstuk: Het werken met Gegevens (DML), WOONPLAATS, TELEFOON WHERE WOONPLAATS = 'UTRECHT' Hierdoor worden de projectie en de selectie gecombineerd. SELECT+PROJECTIE In veel gevallen zal het bovendien zo zijn dat we gegevens uit meerdere tabellen willen combineren. Het proces van normaliseren leidt tenslotte al vrij snel tot een ware explosie van het aantal tabellen. Dat maakt de kans op redundantie vrij klein maar maakt het opvragen van gegevens er in veel gevallen niet gemakkelijker op. Bij het combineren van gegevens uit meerdere tabellen (de JOIN) zullen we ons een aantal zaken moeten realiseren. Het is vooral van belang om te weten in welke volgorde een relationele database (conceptueel gezien uiteraard) de verschillende onderdelen van een SELECT opdracht verwerkt. Als we een eenvoudige SELECT opdracht zoals in het voorafgaande formuleren, zal de verwerking als volgt plaatsvinden: eerst wordt de tabel (of worden de tabellen) in het werkgeheugen geplaatst, met andere woorden: eerst wordt het FROM gedeelte uitgevoerd. Vervolgens zal er een selectie van rijen die aan de voorwaarde voldoen plaatsvinden (het WHERE gedeelte dus) en tenslotte zal gekeken worden welke kolommen uiteindelijk op het beeldscherm moeten worden gezet. Dat betekent dus dat bij een combinatie van gegevens uit meerdere tabellen deze tabellen allereerst in het werkgeheugen worden geplaatst zonder dat er een vorm van selectie heeft plaatsgevonden. Als we gegevens uit meerdere tabellen willen combineren geven we dit aan door in het FROM gedeelte de tabellen, gescheiden door komma's, te noemen. Als we weer terugkeren naar de tabellen uit het eerste hoofdstuk zouden we ons voor kunnen stellen dat we een overzicht 1 7

Hoofdstuk: Het werken met Gegevens (DML) willen hebben van alle uren die verschillende werknemers hebben besteed aan verschillende projecten. We zouden dus kunnen formuleren SELECT *, WERK Als we deze opdracht daadwerkelijk zo in zouden geven, zouden we het volgende resultaat verkrijgen: WERKNEMERS- NAAM WERKNEMERS- PROJECT- UREN CODE CODE CODE 12 Jan 12 110 28 12 Jan 34 110 14 12 Jan 12 120 12 12 Jan 56 130 23 12 Jan 34 120 16 34 Irene 12 110 28 34 Irene 34 110 14 34 Irene 12 120 12 34 Irene 56 130 23 34 Irene 34 120 16 56 Inge 12 110 28 56 Inge 34 110 14 56 Inge 12 120 12 56 Inge 56 130 23 56 Inge 34 120 16 67 Karel 12 110 28 67 Karel 34 110 14 67 Kare1 12 120 12 67 Karel 56 130 23 67 Karel 34 120 16 We hebben dan het product van twee verzamelingen op ons scherm oftewel een cartesiaans product. Dit wil zeggen dat elke rij uit de (eerste) tabel WERKNEMERS is gekoppeld met elke rij van de (tweede) tabel WERK. Dit kan uiteraard niet de bedoeling zijn. We willen immers gegevens uit twee tabellen met elkaar combineren die ook op dezelfde entiteiten betrekking hebben. Dit kunnen we bereiken door deze rijen uit dit cartesiaans product te selecteren. Als we aan de voorgaande opdracht toevoegen dat we alleen geïnteresseerd zijn in gegevens die betrekking hebben op één en dezelfde werknemer, kunnen we dat als volgt formuleren SELECT *, WERK WHERE WERKNEMERS.WERKNEMERSCODE = WERK.WERKNEMERSCODE Als we de opdracht zo formuleren wordt er nog steeds een cartesiaans product gemaakt maar dit wordt alleen in het werkgeheugen gebruikt door het RDBMS. Als dit cartesiaans product dan in het werkgeheugen is geplaatst, worden hier vervolgens alle rijen uit geselecteerd die voldoen aan de gestelde voorwaarde in het WHERE gedeelte. Hierbij valt op dat we de werknemerscode vooraf laten gaan door de naam van de tabel, dus WERKNEMERS.WERKNEMERSCODE=WERK.WERKNEMERSCODE 1 8

Hoofdstuk: Het werken met Gegevens (DML) en niet WERKNEMERSCODE=WERKNEMERSCODE omdat de kolom met als kolomkop WERKNEMERSCODE nu in feite tweemaal voorkomt in het werkgeheugen en we dus aan moeten geven welke van de twee we bedoelen. Het toevoegen van deze voorwaarde heeft tot gevolg dat we nu op ons scherm het volgende te zien krijgen WERKNEMERS NAAM WERKNEMERS PROJECT UREN CODE CODE CODE 12 Jan 12 110 28 12 Jan 12 120 12 34 Irene 34 110 14 34 Irene 34 120 16 56 Inge 56 130 23 En dit lijkt meer op wat we eigenlijk wilden weten. Als we dan bovendien nog een projectie toepassen, kunnen we een aardig resultaat verkrijgen SELECT WERKNEMERS.WERKNEMERSCODE, NAAM, PROJECTCODE, UREN, WERK WHERE WERKNEMERS.WERKNEMERSCODE=WERK.WERKNEMERSCODE Geeft dan WERKNEMERS- NAAM PROJECT- UREN CODE CODE 12 Jan 110 28 12 Jan 120 12 34 Irene 110 14 34 Irene 120 16 56 Inge 130 23 Nu we de fundamentele bewerkingen besproken hebben zullen we in de volgende paragrafen nog eens wat gedetailleerder ingaan op allerlei aspecten van de SELECT instructie. 1 9

Hoofdstuk: Het werken met Gegevens (DML) VRAGEN 1. Welk woord gebruiken we altijd om voorwaarden te stellen aan de te selecteren rijen? 2. Hoe kunnen we een opgevraagd overzicht van rijen beperken tot bepaalde kolommen? 3. Hoeveel rijen zouden we op ons scherm krijgen als we van drie tabellen van ieder 80 rijen op zouden vragen SELECT * FROM TABEL1, TABEL2, TABEL3 4. Wanneer moeten we gebruik maken van de combinatie tabelnaam.kolomnaam? 5. Noem de 3 fundamentele opdrachten die we met een SELECT kunnen uitvoeren. Geef een korte uitleg over de werking ervan. 6. Geef aan wat er achtereenvolgens wordt uitgevoerd bij de volgende opdracht SELECT A.a,A.b,B.a FROM A, B WHERE A.a=B.a OPDRACHTEN 1. Geef de opdracht in SQL om alle gegevens uit de tabel KLANTEN op te vragen. 2. Geef de opdracht in SQL om alle gegevens op te vragen uit de tabellen KLANTEN en BESTELLINGEN. (Zowel in de tabel KLANTEN als in de tabel BESTELLINGEN is er een kolom KLANTCODE.) 2 0

Hoofdstuk: Het werken met Gegevens (DML) DISTINCT Weer uitgaande van de tabel WERKNEMERS zoals we die in een eerdere paragraaf hebben gevuld zouden we een overzicht op kunnen vragen van alle woonplaatsen waar onze werknemers wonen, Met het voorgaande nog in gedachten is deze vraag vrij snel in SQL te formuleren SELECT WOONPLAATS Als we dit zo invoeren krijgen we het volgende resultaat WOONPLAATS AMSTERDAM AMSTERDAM LEEUWARDEN AMSTERDAM VLISSINGEN ROTTERDAM AMSTERDAM UTRECHT EINDHOVEN ROTTERDAM AMSTERDAM MAASTRICHT ROTTERDAM ROTTERDAM AMSTERDAM ROTTERDAM UTRECHT ROTTERDAM EINDHOVEN ROTTERDAM Ondanks het feit, dat dit het enige juiste antwoord op de gestelde vraag is, zal het in veel gevallen eerder de bedoeling zijn om een overzicht te produceren van alle verschillende woonplaatsen uit de tabel WERKNEMERS. Ook dit is in SQL vrij eenvoudig te formuleren SELECT DISTINCT WOONPLAATS Dit zal het volgende resultaat opleveren WOONPLAATS AMSTERDAM LEEUWARDEN VLISSINGEN ROTTERDAM EINDHOVEN MAASTRICHT Door het toevoegen van DISTINCT zorgen we er voor dat er geen dubbele waarden afgedrukt worden. Let wel: DISTINCT geldt per rij, dus als we zouden invoeren 2 1

Hoofdstuk: Het werken met Gegevens (DML) SELECT DISTINCT NAAM,WOONPLAATS levert dit vrijwel alle namen en alle woonplaatsen uit de WERKNEMERS tabel op omdat deze steeds als combinatie wel eenmalig voorkomen. (Welke combinatie van naam en woonplaats komt nu nog steeds te vervallen?) OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. Alle namen van alle werknemers. 2. Alle namen slechts eenmaal. genoemd. 11 3. Alle combinaties van WOONPLAATS en POSTCODE eenmaal genoemd. 4. Alle salarissen. 5. Alle salarissen slechts eenmaal genoemd. 6. Alle waarden uit de DATUM_IN_D kolom. 2 2

Hoofdstuk: Het werken met Gegevens (DML) Rekenen met kolominhouden Het is ook mogelijk om berekeningen uit te voeren met te selecteren kolommen alvorens ze op het scherm af te drukken. We gebruiken daarvoor {involgorde van prioriteit) de operatoren * en / vermenigvuldigen en delen + en -optellen en aftrekken De prioriteit is uiteraard te beïnvloeden door gebruik te maken van haakjes (). We mogen deze operatoren gebruiken om berekeningen uit te voeren met een kolom en een constante, twee kolommen en {hoewel dat niet zo erg nuttig is) twee constanten. Als we naast het maandsalaris ook het jaarsalaris van de WERKNEMERS willen zien kunnen we dat opvragen door,sal,sal * 12 Dat geeft ons het volgende overzicht. NAAM SAL SAL * 12 BUCK 4567 54804 MAAS 3332 39984 MERK 4749 56988 WILLEGEN 4988 59856 KONING 5624 67488 LOOF 4328 51936 VLIET 3848 46176 NEVE 3148 37776 JONGE 2118 25416 JOBSE 4716 56592 VLIET 2638 31656 BRASSER 2149 25788 BOS 2357 28284 MAAS 2849 34188 JANSMA 2144 25728 PAREE 2745 32940 ZEEUW 2981 35772 LIEVENSE 3148 37776 UMBGROVE 2988 35856 GEEL 1921 23052 Als we nog een tweede numerieke kolom in onze tabel zouden hebben (COMMISSIE bijvoorbeeld) zouden we een overzicht kunnen krijgen door,sal,commissie,sal+commissie We moeten hierbij wel waarschuwen voor het feit dat een optelling waar een NULL waarde bij betrokken is ook de waarde NULL oplevert. Immers: als we iets onbekends ergens bij optellen, kunnen we alleen maar aangeven dat dit als uitkomst iets op zal leveren dat ook onbekend is. Met andere woorden: alle WERKNEMERS zonder COMMISSIE krijgen de waarde NULL als 2 3

Hoofdstuk: Het werken met Gegevens (DML) uitkomst in de laatste kolom van bovenstaand overzicht (Met de functie NVL(<kolomnaam>,O) die iedere NULL waarde omzet in het getal 0, kunnen we dit in sommige RDBMS-en oplossen.) Vaak leiden rekenkundige bewerkingen tot lelijke en lange kolomkoppen in onze overzichten. Dit kunnen we oplossen door in het SELECT gedeelte na de rekenkundige formulering een spatie, gevolgd door een alternatieve kolomkop op te geven.,sal PER_MAAND,SAL *12 PERJAAR Dit ziet er meteen al wat fraaier uit dan de formulering zonder alternatieve kolomkoppen. NAAM PER_MAAND PER_JAAR BUCK 4567 54804 MAAS 3332 39984 MERK 4749 56988 WILLEGEN 4988 59856 KONING 5624 67488 LOOF 4328 51936 VLIET 3848 46176 NEVE 3148 37776 JONGE 2118 25416 JOBSE 4716 56592 VLIET 2638 31656 BRASSER 2149 25788 BOS 2357 28284 MAAS 2849 34188 JANSMA 2144 25728 PAREE 2745 32940 ZEEUW 2981 35772 LIEVENSE 3148 37776 UMBGROVE 2988 35856 GEEL 1921 23052 OPDRACHTEN Geef een opdracht in SOL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. De naam en het weeksalaris (salaris maal 3 gedeeld door 13) onder de kolomkop WEEKSAL van iedere werknemer. 2. De naam en het salaris inclusief een op handen zijnde salarisverhoging van 2 % onder de kolomkop NWSAL. 3. De naam en het totaal aan salaris inclusief de in vorige paragraaf genoemde kolom COMMISSIE per jaar van alle werknemers. 4. De naam en de bonus (onder de kolomkop BONUS) voor iedere werknemer. Oe bonus wordt berekend door het aantal jaren in dienst (af te leiden uit de numerieke kolom JAAR_IN_O) te vermenigvuldigen met 1 % van het salaris. 2 4

Hoofdstuk: Het werken met Gegevens (DML) Operatoren in het WHERE gedeelte. Tot nu toe hebben we alleen nog maar gebruik gemaakt van het = teken om voorwaarden te stellen aan de te selecteren rijen. Uiteraard hebben we ook andere operatoren tot onze beschikking. We kunnen gebruik maken van het < of het > teken om andersoortige voorwaarden te stellen. Bijvoorbeeld alle namen van werknemers met een salaris van meer dan 3000 Euro: FROMWERKNEMERS WHERE SAL > 3000 Het < of > teken kunnen we ook gebruiken om twee kolommen met elkaar te vergelijken in plaats van het vergelijken van een kolom met een constante zoals in het voorgaande voorbeeld. Hoewel we niet kunnen zeggen dat dit nu zo'n voor de hand liggende vraag is, zouden we kunnen vragen om de namen van alle werknemers met een code die groter is dan de code van hun chef WHERE CODE > CHEF Ondanks het feit dat deze opdracht in dit geval het juiste antwoord oplevert moeten we toch waarschuwen voor het gebruik van het < en het > teken bij alfanumerieke ((HAR) kolommen. Omdat de ASCII tabel (gelukkig) onderscheid maakt tussen hoofdletters en kleine letters, kan dit nog wel eens tot problemen leiden. Bovendien wordt er nu lexicografisch vergeleken; dat wil zeggen dat eerst gekeken zal worden naar het eerste teken, als dit gelijk is wordt gekeken naar het volgende teken enzovoort. Dat is een prima uitgangspunt voor het indelen van bijvoorbeeld het telefoonboek maar het strookt niet met ons numerieke positiestelsel omdat daarbij de waarde 80 kleiner zal zijn dan 100 terwijl bij een lexicografische vergelijking we bij het eerste teken al constateren dat de 8 in de ASCII tabel een hogere waarde heeft dan de 1 en dat daarmee de waarde 80 dus groter is dan de waarde 100! Bij numerieke (NUMBER) kolommen leveren deze operatoren vanzelfsprekend geen enkel probleem op. Als we het < of > teken willen combineren met het = teken, doen we dit eenvoudigweg door ze achter elkaar te plaatsen WHERE SAL >= 3000 De volgorde van deze twee tekens staat vast, met andere woorden het is niet toegestaan om => te gebruiken in plaats van >=. De 'ongelijk aan' operator wordt_in de verschillende RDBMS-en op verschillende wijzen voorgesteld. De meest voorkomende vormen zijn <>,!= en ^=. In dit boek zullen we het!= teken gebruiken. Zo kunnen we dus alle namen van werknemers die niet in ROTTERDAM wonen opvragen door 2 5

Hoofdstuk: Het werken met Gegevens (DML) WHERE WOONPLAATS!= 'ROTTERDAM' OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. Alle namen en telefoonnummers van werknemers uit AMSTERDAM. 2. Alle namen, adressen en woonplaatsen van werknemers die minder verdienen dan 2600 Euro. 3. Alle namen van werknemers die een naam hebben die in het telefoonboek na de naam JANSEN komt. 4. De woonplaatsen van alle werknemers die niet voor chef 40 werken. 5. De salarissen van alle werknemers die uit MAASTRICHT komen. 6. De namen en adressen van alle werknemers die na 1 januari 1988 in dienst zijn gekomen. 7. De namen en 10% van het salaris (onder de kolomkop SAL_DEEL) van alle werknemers die niet in ROTTERDAM wonen. 8. Alle woonplaatsen (zonder dubbelen) van werknemers die voor 31 december 1987 in dienst zijn gekomen. 9. Alle namen en telefoonnummers van werknemers met een postcode boven 3088. 10. Alle namen van werknemers die niet meer dan 3500 Euro verdienen. 2 6

Hoofdstuk: Het werken met Gegevens (DML) Het combineren van operatoren met AND en OR Vaak zullen we niet kunnen volstaan met het stellen van één voorwaarde maar zullen we meerdere voorwaarden willen combineren. In SQL kunnen we dan gebruik maken van AND en OR. Zo zouden we ons af kunnen vragen welke werknemers uit ROTTERDAM aangenomen zijn voor 1 januari 1987. WHERE WOONPLAATS = 'ROTTERDAM' AND DATUM_IN_D < '01-JAN-87' Dat is een geheel andere vraag dan de vraag welke werknemers uit ROTTERDAM komen OF voor 1 januari 1987 zijn aangenomen WHERE WOONPLAATS = 'ROTTERDAM' OR DATUM_IN_D < '01-JAN-87' Bij het gecombineerd gebruiken van de AND en OR operatoren moeten we enige voorzichtigheid in acht nemen. Stel dat we een overzicht willen van alle werknemers die voor 1 januari 1987 in dienst zijn gekomen en uit ROTTERDAM of uit UTRECHT komen, dan levert de opdracht WHERE DATUM_IN_D < '01-JAN-87' AND WOONPLAATS = 'ROTTERDAM' OR WOONPLAATS = 'UTRECHT' niet het juiste antwoord op. Zo geformuleerd vragen we immers om de namen van alle werknemers die voor 1 januari 1987 in dienst zijn gekomen en in ROTTERDAM wonen, aangevuld met de namen van werknemers die in UTRECHT wonen. We kunnen dit probleem op twee manieren oplossen WHERE DATUM_IN_D < '0l-JAN-87' AND WOONPLAATS = 'ROTTERDAM! OR DATUM_IN_D < '01-JAN-87'AND WOONPLAATS = 'UTRECHT' of WHERE DATUM_IN_D < '01-JAN-87! AND (WOONPLAATS = 'ROTTERDAM' OR WOONPLAATS =!UTRECHT') In veel gevallen zullen we, vanwege de kortere formulering, voor de tweede oplossing kiezen: het gebruik van haakjes. 2 7

Hoofdstuk: Het werken met Gegevens (DML) OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. De namen van alle werknemers die in UTRECHT wonen en een salaris hebben van meer dan 3000 Euro. 2. De namen van alle werknemers met een postcode tussen (inclusief de grens waarden) 3050 en 3100. 3. De namen van alle werknemers met een salaris beneden de 2500 Euro of een salaris boven de 4000 Euro. 4. De namen van alle werknemers met een salaris tussen de 2500 en 4000 Euro. 5. De namen en woonplaatsen van alle werknemers met als woonplaats ROTTERDAM en als postcode 3088 JP of 3085 HG. 6. De namen van alle werknemers met als chef chef nummer 99 of chef nummer 20 en als salaris een salaris van meer dan 4000 Euro of minder dan 2500 Euro. 7. De code en de naam van alle werknemers die niet voor chef 40 werken of een salaris tussen 3000 en 4000 Euro hebben. 8. De naam, woonplaats en datum van indiensttreding van alle werknemers die tussen 1 januari 1987 en 31 december 1988 in dienst zijn gekomen of in AMSTERDAM wonen. 9. De namen van alle werknemers die in UTRECHT wonen of een salaris hebben tussen 2100 en 3000 Euro. 10. De namen van alle werknemers die niet in ROTTERDAM of in UTRECHT wonen, niet voor chef 99 werken en een salaris tussen 2000 en 3000 Euro hebben. 2 8

Hoofdstuk: Het werken met Gegevens (DML) Het gebruik van NOT Naast de!= operator kunnen we ook gebruik maken van NOT om een ontkenning te formuleren. Dit wil echter niet zeggen dat de NOT operator een zelfde werking heeft als de!= operator!! We zouden ons bijvoorbeeld af kunnen vragen welke werknemers niet in ROTTERDAM of AMSTERDAM wonen. Als we dat proberen te formuleren als WHERE WOONPLAATS!= 'ROTTERDAM' OR WOONPLAATS!= 'AMSTERDAM' hebben we weliswaar een ontkennende operator in een met OR verbonden stel voorwaarden geplaatst maar deze opdracht zal alle werknemers uit de WERKNEMERS tabel op ons scherm zetten omdat voor alle werknemers geldt dat hun woonplaats of ongelijk aan AMSTERDAM of ongelijk aan ROTTERDAM is; dat geldt zelfs voor ROTTERDAM en AMSTERDAM zelf. De vraag is wel te beantwoorden met WHERE NOT (WOONPLAATS = 'ROTTERDAM' OR WOONPLAATS = 'AMSTERDAM') De NOT operator heeft een hogere prioriteit dan de AND operator, die op zijn beurt weer boven de OR operator gaat. In de volgende evaluatietabel kunnen we zien hoe de NOT, AND en OR operatoren gebruikt moeten worden. Met ONBEKEND wordt bedoeld dat de waarde niet is ingevuld oftewel NULL is. A B NOT A A AND B A OR B WAAR ONWAAR ONWAAR WAAR WAAR WAAR ONWAAR ONWAAR ONWAAR WAAR WAAR ONBEKEND ONWAAR ONBEKEND WAAR ONWAAR WAAR WAAR ONWAAR WAAR ONWAAR ONWAAR WAAR ONWAAR ONWAAR ONWAAR ONBEKEND WAAR ONBEKEND ONBEKEND ONBEKEND WAAR ONBEKEND ONBEKEND WAAR ONBEKEND ONWAAR ONBEKEND ONWAAR ONBEKEND ONBEKEND ONBEKEND ONBEKEND ONBEKEND ONBEKEND 2 9

Hoofdstuk: Het werken met Gegevens (DML) VRAGEN EN OPDRACHTEN VRAGEN Wat is het verschil tussen de volgende twee opdrachten WHERE CHEF!= '99' OR CHEF = '40' WHERE NOT (CHEF = '99' OR (CHEF = '40') OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel. 1. Alle namen van werknemers die niet voor 1 april 1988 in dienst zijn gekomen. 2. Alle namen van werknemers die een salaris hebben dat niet tussen de 2500 en 3500 Euro ligt. 3. Alle namen van de werknemers die niet in MAASTRICHT of UTRECHT wonen en niet voor chef 40 of chef 99 werken. 4. Alle namen van werknemers die niet als chef chef 99 of chef 20 hebben en een salaris van meer dan 4000 of minder dan 2000 Euro hebben. 5. Alle namen van werknemers die niet in 1988 in dienst zijn gekomen. 3 0

Hoofdstuk: Het werken met Gegevens (DML) BETWEEN Als we op zoek zijn naar rijen uit de tabel waarbij de waarde van een kolom tussen twee waarden ligt, zouden we gebruik kunnen maken van >= en <= gekoppeld met AND. Zo zouden we de namen van alle werknemers met een salaris tussen de 2500 en de 3000 Euro op kunnen vragen met WHERE SAL >= 2500 AND SAL <=3000 Voor dit soort vragen kent SQL de BETWEEN operator: WHERE SAL BETWEEN 2500 AND 3000 Deze formulering is overzichtelijker dan de voorgaande met de >= en <= tekens. Wel moeten we ons realiseren dat BETWEEN...AND...niet letterlijk TUSSEN...EN...betekent omdat ook de grenswaarden meegenomen worden. Uiteraard kunnen we dit oplossen. Door te formuleren BETWEEN 2501 AND 2999 of iéts dergelijks. Met behulp van de BETWEEN operator in combinatie met NOT kunnen we uiteraard ook vragen stellen als: Welke werknemers hebben een salaris dat niet tussen de 2500 en 3000 Euro ligt. WHERE SAL NOT BETWEEN 2500 AND3000 Geformuleerd zonder gebruik te maken van de BETWEEN operator zouden we moeten stellen: WHERE SAL < 2500 OR SAL > 3000 Merk op dat in dit geval gebruik gemaakt wordt van OR om de twee voorwaarden aan elkaar te koppelen. OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. Alle namen van werknemers die in dienst gekomen zijn tussen 1 januari 1985 en 31 december 1986. 2. Alle namen van werknemers die een salaris tussen 3500 en 4500 Euro hebben en in ROTTERDAM wonen. 3. Alle namen van werknemers die een salaris hebben dat niet tussen 4000 en 5000 Euro ligt of in UTRECHT wonen. 3 1

Hoofdstuk: Het werken met Gegevens (DML) IN Vaak zullen we op zoek zijn naar rijen waarbij een bepaalde kolom gelijk moet zijn aan een aantal mogelijke waarden. We zouden bijvoorbeeld op zoek kunnen zijn naar alle werknemers die in ROTTERDAM, AMSTERDAM of UTRECHT wonen. We kunnen dit vrij eenvoudig formuleren door te vragen WHERE WOONPLAATS = 'ROTTERDAM' OR WOONPLAATS = 'AMSTERDAM' OR WOONPLAATS =}UTRECHT' Het zal duidelijk zijn dat dit, zeker bij herhaald gebruik van dit soort samengestelde voorwaarden, niet erg prettig werkt. Gelukkig hebben we de beschikking over de IN operator waarmee we dergelijke vragen vrij eenvoudig op kunnen lossen WHERE WOONPLAATS IN ('ROTTERDAM', 'AMSTERDAM','UTRECHT') Met behulp van de IN operator kunnen we dus aan een kolom de voorwaarde stellen dat de daar ingevulde waarde voor moet komen in een reeks van waarden die we, gescheiden door komma's, tussen haakjes plaatsen na de IN operator. Het spreekt voor zich dat we ook hier weer gebruik kunnen maken van NOT om de voorwaarde ontkennend te maken. OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. De namen van werknemers die voor chef 99, 40 of 30 werken. 2. De namen van werknemers met als code 10, 12 of 60 en een salaris tussen de 3500 en 5000 Euro. 3. De namen van werknemers die in UTRECHT of MAASTRICHT wonen en niet voor chef 99 of chef 40 werken 4. De namen van werknemers die meer dan 4000 Euro verdienen en in ROTTERDAM, UTRECHT of MAASTRICHT wonen. 5. De namen van werknemers die niet in AMSTERDAM of UTRECHT wonen en in 1988 in dienst zijn gekomen. 3 2

Hoofdstuk: Het werken met Gegevens (DML) LIKE In sommige gevallen krijgen we te maken met voorwaarden die niet zo gemakkelijk te vangen zijn in vraagstellingen zoals we die tot nu toe behandeld hebben. Met name geldt dit voor vragen als: welke werknemers hebben een naam die begint met een 'V' of hebben een naam met als derde letter een' A '? Voor dit soort vragen maken we gebruik van de LIKE operator tezamen met de % en _ tekens. Het procentteken (%) staat daarbij voor een willekeurige rij lettertekens terwijl het onderstrepingsteken (_) staat voor een willekeurig letterteken. Bovenstaande vragen zijn dus als volgt te formuleren: WHERE NAAM LIKE 'V%' en WHERE NAAM LIKE ' A%' Zo kunnen we dus ook de voorwaarde stellen dat de naam niet mag bestaan uit 5 lettertekens door gebruik te maken van: WHERE NAAM NOT LIKE ' ' OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. Alle namen van werknemers met een naam beginnend met een B. 2. Alle namen van werknemers met een naam met als vijfde letter een E. 3. Alle namen van werknemers met een naam waar op de laatste plaats niet de letter E staat. 4. Alle namen van werknemers met een naam van vier letters. 5. Alle woonplaatsen die niet eindigen op 'DAM'. 6. Alle namen van werknemers die in een 'LAAN' wonen (dat wil zeggen: niet in een straat of plein of iets dergelijks). 7. Alle namen van werknemers met een postcode die begint met 30. 3 3

Hoofdstuk: Het werken met Gegevens (DML) NULL Als we op zoek zijn naar rijen in een tabel waarbij we als voorwaarde stellen dat een bepaalde ko1om oningevuld moet zijn, met andere woorden dat deze kolom de waarde onbekend oftewel NULL moet hebben, maken we gebruik van de ISNULL voorwaarde. We zouden bijvoorbeeld kunnen kijken welke werknemers geen CHEF hebben door in te voeren: II WHERE CHEF IS NULL We dienen ons (zoals reeds opgemerkt) goed te realiseren dat de NULL waarde betekent onbekend wat dus niet hetzelfde is als de waarde 0. Zo zal de opdracht FROMWERKNEMERS WHERE CHEF <= '99' niet alle rijen uit de tabel WERKNEMERS selecteren omdat de niet ingevulde waarde bij werknemer DE KONING niet kleiner is dan de waarde '99' maar eenvoudigweg ONBEKEND is zodat deze vraagstelling slechts 19 van de 20 rijen oplevert. OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. De namen van werknemers die geen voorvoegsels bij hun naam hebben. 2. De namen van werknemers die wel een chef hebben. 3. De namen van werknemers waarvan het telefoonnummer onbekend is. 4. De namen van werknemers waarvan het salaris of de chef (nog) riet bekend is. 5. De namen van werknemers waarvan het salaris (nog) niet bekend is maar die wel een chef hebben. 3 4

Hoofdstuk: Het werken met Gegevens (DML) ORDER BY Als we het resultaat van een bepaalde vraagstelling ook nog willen sorteren, kunnen we gebruik maken van de opdracht ORDER BY. Deze optie wordt altijd als laatste onderdeel van een SELECT opdracht opgegeven. Eigenlijk is het niets anders dan een laatste bewerking van de geselecteerde rijen. Als we de namen van alle werknemers uit ROTTERDAM, alfabetisch geordend, willen zien gebruiken we WHERE WOONPLAATS ='ROTTERDAM' ORDER BY NAAM Het is ook mogelijk om op meer dan één waarde te ordenen: ORDER BY NAAM,SAL Dit zal de namen van de werknemers geordend op naam (en bij gelijke namen op salaris) weergeven. Daarmee meteen aangevend dat het ordenen dus zowel op alfanumerieke als numerieké kolommen kan gebeuren. Dat geldt overigens ook voor datumkolommen. Uiteraard geldt ook hier weer het gevaar van een lexicografische ordening van alfanumerieke waarden die uit cijfers bestaan. We kunnen deze volgorde ook omdraaien (van Z naar A laten sorteren) door achter ORDER BY NAAM DESC (van DESCENDING) op te geven. Om de sorteervolgorde van A naar Z te laten lopen zouden we ASC (van ASCENDING) op kunnen geven maar aangezien dat de default (door het systeem gekozen) volgorde is doen we dit meestal niet. Een apart probleem vormen de NULL waarden bij het gebruik van ORDER BY. Bij verschillende RDBMS-en wordt hier verschillend mee omgegaan. In ORACLE bijvoorbeeld komen de NULL waarden altijd eerst, of er nu oplopend of aflopend wordt gesorteerd. Dat betekent dus dat bij de opdracht ORDER BY CHEF werknemer DE KONJNG als eerste op het scherm zal komen, ook als we opgeven ORDER BY CHEF DESC 3 5

Hoofdstuk: Het werken met Gegevens (DML) OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. De namen van alle werknemers oplopend geordend naar hun code. 2. Alle woonplaatsen slechts eenmaal genoemd, alfabetisch aflopend geordend. 3. Alle namen van alle werknemers, aflopend geordend naar salaris. 4. Alle namen van alle werknemers, geordend naar datum van indiensttreding. 5. Alle namen van alle werknemers met een salaris tussen 2000 en 4000 Euro, geordend naar woonplaats en per woonplaats naar naam. 3 6

Hoofdstuk: Het werken met Gegevens (DML) GROUP BY, HAVING, COUNT, AVG, SUM, MAX en MIN De GROUP BY instructie zorgt er voor dat bepaalde rijen gegroepeerd worden zodat we berekeningen en selecties kunnen uitvoeren met groepen rijen in plaats van individuele rijen. We zullen ons eerst eens af gaan vragen hoe de GROUP BV instructie nu eigenlijk werkt. Omdat de GROUP BV instructie onmiddellijk na de WHERE component wordt uitgevoerd kunnen we ons het volgende voorstellen. We zouden van de rijen in de tabel WERKNEMERS een aantal gegroepeerde rijen kunnen maken, bijvoorbeeld per WOONPLAATS. Om dit te bereiken zouden we dus na de WHERE component toe moeten voegen GROUP BY WOONPLAATS Het gevolg hiervan is dat er als het ware per woonplaats een aantal rijen tot één rij gecombineerd worden. Dit betekent automatisch dat we met een opdracht als,woonplaats GROUP BY WOONPLAATS niets kunnen beginnen. Immers er wordt gegroepeerd, dus eigenlijk ontstaat er iets als het volgende NAAM BUCK,BRASSER, VLIET,NEVE,PAREE,JANSMA UMBGROVE LIEVENSE MMS,MERK, WILLEGEN,JOBSE,BOS, MMS,KONING GEEL,LOOF ZEEUW, VLIET JONGE WOONPLAATS AMSTERDAM VLISSINGEN LEEUWARDEN ROTTERDAM UTRECHT EINDHOVEN MAASTRICHT Het op het scherm zetten van de woonplaatsen is niet zo'n probleem. Maar wat moet er nu afgedrukt worden bij NAAM?? Dit kan dus niet We zullen er dus rekening mee moeten houden dat we bij de GROUP BY optie alleen vragen om die kolommen waar op gegroepeerd wordt of gebruik maken van de zogenaamde GROUP FUNCTIES. De GROUP FUNCTIES die we in iedere SQL implementatie aan kunnen treffen zijn: COUNT AVG SUM MAX MIN 3 7

Hoofdstuk: Het werken met Gegevens (DML) Count We zouden ons bijvoorbeeld af kunnen vragen hoeveel werknemers er in iedere plaats wonen SELECT WOONPLAATS,COUNT(*) GROUP BV WOONPLAATS Dit heeft tot gevolg dat er bij het tussen resultaat gekeken zal worden hoeveel rijen er in iedere groep zitten (we geven door het* teken aan dat het aantal rijen geteld moet worden) en dat de uitkomst op het scherm afgedrukt zal worden. Dat geeft dan het volgende resultaat WOONPLAATS COUNT(*) AMSTERDAM 6 VLISSINGEN 1 LEEUWARDEN 1 ROTTERDAM 7 UTRECHT 2 EINDHOVEN 2 MAASTRICHT 1 We kunnen COUNT ook gebruiken om alleen het aantal ingevulde waarden per kolom te tellen. We gebruiken dan de kolomnaam in plaats van het * teken. SELECT WOONPLAATS,COUNT(CHEF) GROUP BY WOONPLAATS Geeft ons per woonplaats het aantal werknemers met een CHEF. Met andere woorden: ROTTERDAM zal dan door het getal 6 in plaats van 7 worden vergezeld omdat de in ROTTERDAM woonachtige DE KONING geen CHEF heeft en in dit geval dus niet meetelt. AVG De functie AVG wordt gebruikt om het gemiddelde (AVERAGE) van een bepaalde groepte berekenen. Zo zouden we het gemiddelde salaris per woonplaats op kunnen vragen door SELECT WOONPLAATS,AVG(SAL) GROUP BY WOONPLAATS I AVG berekent het gemiddelde en houdt daarbij rekening met het feit dat niet ingevulde kolommen (NULL waarden) genegeerd moeten worden. Ook is het in sommige RDBMS-en mogelijk om DISTINCT te gebruiken zodat het gemiddelde van alle verschillende waarden berekend wordt. (AVG(DISTINCT(GETAL)) bij de waarden 1,1,1 en 3 levert dan 2 op in plaats van 1,5.) SUM De SUM functie totaliseert een numerieke kolom SELECT WOONPLAATS,SUM(SAL) GROUP BY WOONPLAATS levert per WOONPLAATS het totaal aan salarissen op. 3 8

Hoofdstuk: Het werken met Gegevens (DML) MAX en MIN De MAX en de MIN functie berekenen respectievelijk de hoogste en de laagste waarde binnen een groep. SELECT WOONPLAATS,MAX(SAL) GROUP BY WOONPLAATS geeft ons per woonplaats het hoogste salaris, terwijl SELECT WOONPLAATS,MIN(SAL) GROUP BY WOONPLAATS ons het laagste salaris per woonplaats oplevert. Het is ook toegestaan om te groeperen op meerdere kolommen. Als we bijvoorbeeld een overzicht willen hebben van het aantal werknemers per woonplaats en binnen woonplaats per postcode gebied kunnen we opvragen SELECT WOONPLAATS,POSTCODE,COUNT(*) GROUP BY WOONPLAATS,POSTCODE De GROUP FUNCTIES kunnen ook gebruikt worden zonder het GROUP BY gedeelte. Dat komt met name van pas als we zo n functie willen gebruiken voor een hele tabel. We beschouwen dan in feite de gehele tabel als een groep. De opdracht SELECT COUNT(*), AVG(SAL), SUM(SAL), MAX(SAL), MIN(SAL) geeft ons een overzicht van achtereenvolgens het aantal rijen, het gemiddelde salaris, het totaalbedrag van alle salarissen, het hoogste salaris en het laagste salaris van alle werknemers in de WERKNEMERS tabel. Aan gegroepeerde rijen kunnen we ook typische groepsvoorwaarden' stellen. Zo zouden we bijvoorbeeld het aantal werknemers per woonplaats op kunnen vragen maar daar tegelijkertijd aan toe kunnen voegen dat we in het overzicht alleen die woonplaatsen willen zien die meer dan 5 werknemers hebben. 3 9

Hoofdstuk: Het werken met Gegevens (DML) HAVING Dan moeten we gebruik maken van HAVING. SELECT WOONPLAATS,COUNT(*) GROUP BY WOONPLAATS HAVING COUNT(*) > 5 Dat geeft dan het volgende resultaat WOONPLAATS COUNT(*) AMSTERDAM 6 ROTTERDAM 7 Het RDBMS zal nog steeds de eerste stappen uit de eerder genoemde opdracht zonder HAVING uitvoeren, maar zal voordat het resultaat op het scherm wordt afgedrukt als het ware nog die groepen uitfilteren die niet aan de, in het HAVING gedeelte gestelde, voorwaarde voldoen. Uiteraard is het toegestaan om in een SELECT opdracht met een GROUP BY voorwaarden te stellen met behulp van een WHERE gedeelte. We moeten ons dan goed realiseren dat het WHERE gedeelte eerder wordt uitgevoerd dan het GROUP BY gedeelte, zodat er groepen gemaakt worden van alleen die rijen die aan de, in het WHERE gedeelte gestelde, voorwaarde voldoen. SELECT WOONPLAATS,AVG(SAL) WHERE SAL>2500 GROUP BY WOONPLAATS Geeft ons per woonplaats het gemiddelde salaris van iedereen die meer dan 2500 Euro verdient. Apart aandacht verdient nog de waarde NULL oftewel de niet ingevulde waarde. Bedenk dat er dus ook een groep kan ontstaan van records waarbij in de kolom die we in het GROUP BY gedeelte gebruiken, niets is ingevuld. 4 0

Hoofdstuk: Het werken met Gegevens (DML) OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS tabel 1. Per woonplaats het gemiddelde salaris. 2. Het hoogste salaris dat per chef door een ondergeschikte verdiend wordt. 3. Idem als vraag 2 maar nu tellen alleen de werknemers die minder dan 4000 Euro verdienen mee. 4. Het aantal werknemers dat onder iedere chef valt, chefs met minder dan twee ondergeschikten moeten niet vermeld worden 5. Die woonplaatsen waar het gemiddelde salaris hoger is dan 2600 Euro. 4 1

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN HET RAADPLEGEN VAN MEERDERE TABELLEN De verschillende mogelijkheden Voor het raadplegen van gegevens uit meerdere tabellen hebben we de beschikking over een aantal mogelijkheden. In de eerste plaats kunnen we natuurlijk gebruik maken van de eerder besproken JOIN. Naast de JOIN kennen we de SUBQUERY, die ook gebruikt kan worden voor het raadplegen van gegevens uit één tabel maar eigenlijk het best tot zijn recht komt bij het gebruik van meerdere tabellen. Bovendien zullen we zien dat we in veel gevallen zowel een JOIN als een SUBQUERY kunnen gebruiken om een antwoord te vinden op een bepaalde vraag. Tenslotte hebben we dan ook de UNION, MINUS EN INTERSECT mogelijkheden. Met deze opdrachten doen we eigenlijk niets anders dan het bewerken van de resultaten van twee SELECT opdrachten. We zullen in paragraaf 4.4 stilstaan bij deze opdrachten. Voordat we deze verschillende mogelijkheden gaan bespreken zullen we nog een drietal tabellen aan onze database gaan toevoegen. We zullen volstaan met het geven van de CREATE opdrachten waarmee deze tabellen gemaakt zijn en een overzicht geven van de ingevulde waarden. Om te beginnen maken we een tabel PROJECTEN aan. Deze tabel is, in een enigszins verkorte vorm, bij het normaliseren al eens aan de orde geweest. CREATE TABLE PROJECTEN ( PROJ_CODE CHAR(3) NOT NULL, PROJ_NAAM CHAR(15) NOT NULL, PROJ_PLAATS CHAR(20), PROJ_LEIDER CHAR(3), STARTDATUM DATE, BUDGET NUMBER(6) ) Indien de data dictionaire hiertoe geen mogelijkheden biedt, kunnen we de sleutel kolom beschermen door CREATE UNIQUE INDEX PROJ_PROJ_CODE ON PROJECTEN(PROJ_CODE) De volgende waarden zijn ingevuld: PROJ_CODE PROJ_NAAM PROJ_PLAATS PROJ_LEIDER START_DATUM BUDGET 110 ITCZ ROTTERDAM 40 01-JAN-95 100.000 120 MTS ROTTERDAM 10 15-AUG-95 145.000 130 CAIA AMSTERDAM 30 01-OCT-96 80.000 140 GABD UTRECHT 60 01-JAN-96 98.000 150 ZVH MAASTRICHT 35 15-FEB-96 112.000 Vervolgens maken we een tabel WERK aan die de gegevens over de werkzaamheden van de verschillende werknemers aan de verschillende projecten vastlegt. Deze tabel brengt dus een verband aan tussen de tabel WERKNEMERS en de tabel PROJECTEN. CREATE TABLE WERK ( PROJ_CODE CHAR(3) NOT NULL, CODE CHAR(3) NOT NULL, UREN NUMBER(4) ) 4 2

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN Ook hier kunnen we de (samengestelde) sleutel beschermen met CREATE UNIQUE INDEX WERK_PRJODE ON WERK(PROLCODE,CODE) Ingevuld zijn de volgende waarden PROJ_CODE CODE UREN 110 40 120 110 25 48 110 45 90 110 26 28 110 10 12 120 10 80 120 50 76 120 20 18 120 12 63 120 26 18 120 40 38 130 30 26 130 26 70 140 60 50 140 48 81 140 26 26 150 35 90 150 47 22 150 28 10 150 26 18 Tenslotte maken we nog een tabel aan waarin we vastleggen welke SALARISSCHALEN er in onze organisatie zijn. We leggen uiteraard het nummer van de schaal en de hoogste en laagste salarissen binnen iedere schaal vast. CREATE TABLE SCHALEN ( SCHAAL CHAR(2) NOT NULL, LAAGSTE NUMBER(8,2), HOOGSTE NUMBER(8,2) ) Vervolgens eventueel CREATE UNIQUE INDEX SCHALEN_SCH ON SCHALEN(SCHAAL) Ingevuld geeft dat SCHAAL LAAGSTE HOOGSTE 1 1600,00 1999,99 2 2000,00 2299,99 3 2300,00 2599,99 4 2600,00 2899,99 5 2900,00 3199,99 6 3200,00 3499,99 7 3500,00 3799,99 8 3800,00 4099,99 9 4100,00 4499,99 10 4500,00 4999,99 11 5000,00 5499,99 12 5500,00 5999,99 13 6000,00 6499,99 14 6500,00 9999,99 4 3

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN De JOIN Zoals we in paragraaf 3.2 al hebben kunnen zien is het mogelijk om gegevens uit meerdere tabellen te raadplegen door gebruik te maken van de JOIN. Dat wil zeggen dat we achter het FROM gedeelte meerdere tabellen noemen en vervolgens (om het cartesiaans produkt te beperken) in het WHERE gedeelte nadere voorwaarden stellen. De meest voor de hand liggende en daardoor ook meest gebruikte vorm is die waarbij we stellen dat alle kolommen in alle rijen van het cartesiaans produkt betrekking hebben op dezelfde entiteit. Dit kunnen we doen door te stellen dat de inhoud van twee kolommen die in beide tabellen voorkomen, gelijk moeten zijn aan elkaar. We noemen dit ook wel de EQUIJOIN vanwege het = teken dat gebruikt wordt. Als we dit willen doen, moeten we er uiteraard voor zorgen dat er in beide tabellen een overeenkomstige kolom voorkomt. Als er een goede gegevensanalyse en een goede normalisatie (zie DEEL 1) heeft plaatsgevonden is dat ook het geval. In onze database is er een verband aan te brengen tussen de tabel WERKNEMERS en de tabel WERK doordat in beide tabellen de kolom CODE voorkomt. Bovendien kunnen we een verband aanbrengen tussen de tabellen PROJECTEN en WERK door middel van de kolom PROJ_CODE. Uiteraard is het dan ook mogelijk om een verband aan te brengen tussen WERKNEMERS en PROJECTEN via de tabel WERK. Schematisch hebben we dus de volgende situatie Als we nu bijvoorbeeld gegevens op willen vragen uit de tabellen WERKNEMERS en WERK, laten we zeggen de CODE van de werknemers, hun NAAM en de PROJJODE met het aantal UREN dat de werknemer aan dat project heeft gewerkt gebruiken we SELECT WERKNEMERS.CODE,NAAM,PROJ_CODE,UREN,WERK WHERE WERKNEMERS.CODE = WERK.CODE We moeten weer rekening houden met het feit dat de kolom CODE zowel in WERKNEMERS als in WERK voorkomt dus moeten we aangeven welke CODE kolom we bedoelen. In het bovenstaande voorbeeld is het uiteraard net zo goed mogelijk om te vragen WERK.CODE af te drukken in plaats van WERKNEMERS.CODE. Het zal ook duidelijk zijn dat een werknemer die aan meerdere projecten werkt nu ook meerdere malen in ons overzicht voor zal komen. Zijn of haar CODE is immers een aantal malen gelijk aan een CODE in de tabel WERK. We hadden natuurlijk ook op kunnen geven SELECT WERKNEMERS.CODE,WERKNEMERS.NAAM, WERK. PROJ_ CODE, WERK. UREN,WERK WHERE WERKNEMERS.CODE = WERK.CODE maar omdat de kolommen NAAM,PROJ_CODE en UREN niet in beide tabellen voorkomen en er dus geen verwarring mogelijk is, laten we de tabelaanduiding meestal achterwege. 4 4

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN Het is uiteraard ook mogelijk om een JOIN te maken van meer dan twee tabellen. We zouden de voorgaande vraag kunnen uitbreiden door ook de PROJ_NAAM in onze vraagstelling op te nemen. We krijgen dan: SELECT WERKNEMERS.CODE, NAAM, PROJECTEN. PROJ_CODE, PROJ_NAAM,UREN,WERK,PROJECTEN WHERE WERKNEMERS.CODE = WERK.CODE AND WERK.PROJ_CODE = PROJECTEN.PROJ_CODE Om dit geheel wat in te korten (en omdat we deze mogelijkheid in het vervolg nog nodig hebben) kunnen we gebruik maken van zogenaamde ALIAS namen voor de tabellen. Dat geven we aan door na de naam van de tabel in het FROM gedeelte een spatie en vervolgens de alternatieve naam op te geven. Zo zouden we de vorige opdracht ook hebben kunnen formuleren als SELECT A.CODE, NAAM, C.PROJ_CODE, PROJ_NAAM, UREN A,WERK B,PROJECTEN C WHERE A.CODE = B.CODE AND B.PROJ_CODE = C.PROJ_CODE 4 5

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS, WERK, PROJECTEN en SCHALEN tabellen 1.De codes, namen (van de werknemers) en de projectcodes van de projecten waar de werknemers aan werken. 2.De projectcodes, projectnamen en medewerkerscodes van alle projecten. 3.De codes, namen, projectcodes, projectnamen en de bestede uren per werknemer. 4. De namen en de bestede uren van alle werknemers. 5. De namen en de bestede uren van alle werknemers uit UTRECHT. 6.De projectnamen van alle projecten waar werknemers die in ROTTERDAM wonen aan meewerken. 7.De namen van alle werknemers die aan een project in AMSTERDAM werken. 4 6

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN De OUTERJOIN In sommige gevallen komt het voor dat we een overzicht willen hebben van gegevens uit meerdere kolommen en daarbij ook gegevens willen zien van entiteiten die niet in beide tabellen voorkomen. Als we naar het voorbeeld uit de vorige paragraaf kijken zien we dat de werknemers die niet aan een project werken en wiens CODE dus niet voorkomt in de tabel WERK niet afgebeeld worden in ons overzicht. Hun CODE is immers in geen enkel geval gelijk aan een CODE uit de WERK tabel! Als we deze werknemers toch in ons overzicht willen betrekken, is daar in de standaard SQL opdrachten geen mogelijkheid voor. Sommige RDBMS en zoals ORACLE geven ons die mogelijkheid wel door de zogenaamde OUTERJOIN. De OUTERJOIN is een vorm van de EQUIJOIN waarbij we als het ware zeggen: 'Als het zo is dat een kolomwaarde die in één van de twee tabellen voorkomt (en gebruikt wordt in het WHERE gedeelte) niet voorkomt in de andere tabel, neem deze rij dan toch éénmalig op in het uiteindelijke overzicht' We geven dit aan door in het WHERE gedeelte, na de naam van de kolom waar de waarde eventueel niet in voorkomt, een tussen haakjes geplaatst + teken te zetten. De vraag uit de vorige paragraaf zou er dan als volgt uit komen te zien SELECT WERKNEMERS.CODE,NAAM, PROJECTEN.PROJ_CODE, PROJ_NAAM,UREN,WERK,PROJECTEN WHERE WERKNEMERS.CODE = WERK.CODE(+) AND WERK.PROJ_CODE = PROJECTEN.PROJ_CODE(+) Hierdoor worden dus ook de werknemers die niet aan een project werken in ons overzicht opgenomen. Bij de kolommen PROJ_CODE, PROJ_NAAM en UREN wordt dan de waarde NULL in ons overzicht geplaatst. De AUTOJOIN Er is een aantal vragen dat ook met de tot nu toe behandelde mogelijkheden niet op te lossen is. Dat geldt met name voor vragen waarbij we eigenlijk voorwaarden willen stellen die betrekking hebben op dezelfde tabel als waar we gegevens uit raadplegen. Dat is bijvoorbeeld het geval bij een vraag als: Welke werknemers hebben een hoger salaris dan hun chef? We willen dan eigenlijk de werknemers koppelen aan hun chef om te kunnen testen of hun salaris hoger is dan het salaris van die chef. Dat zou geen problemen opleveren als we een aparte CHEFS tabel tot onze beschikking zouden hebben. We zouden dan immers de volgende situatie hebben: WERKNEMERS CHEFS CODE NAAM SAL CHEF CODE SAL 10 JAN 100 20 20 80 20 IRENE 80 30 90 30 KAREL 90 40 INGE 110 30 50 PETER 60 30 4 7

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN Als we dan willen weten welke werknemer meer verdient dan zijn of haar chef vragen we eenvoudigweg,chefs WHERE WERKNEMERS.CHEF=CHEFS.CODE AND WERKNEMERS.SAL>C HEFS.SAL De JOIN van de tabellen WERKNEMERS en CHEFS levert het volgende tussenresultaat op (we koppelen alle rijen uit de twee tabellen waarvoor geldt dat de waarde ingevuld bij de kolom CHEF in de WERKNEMERS tabel gelijk is aan de waarde in de kolom CODE in de CHEFS tabel, en hebben dus de werknemers en hun chefs aan elkaar gekoppeld) CODE NAAM SAL CHEF CODE SAL 10 JAN 100 20 20 80 40 INGE 110 30 30 90 50 PETER 60 30 30 90 Hier wordt dan vervolgens de voorwaarde WERKNEMERS. SAL>CHEFS.SAL op toegepast. Dat levert het volgende tussenresultaat op CODE NAAM SAL CHEF CODE SAL 10 JAN 100 20 20 80 40 INGE 110 30 30 90 Tenslotte volgt er dan nog de, inmiddels welbekende, PROJECTIE en dat geeft als eindresultaat NAAM JAN INGE Maar helaas heeft het proces van normalisatie er toe geleid dat er geen aparte tabel CHEFS is. Dat zorgt er voor dat er zo weinig mogelijk gegevens dubbel (dus redundant) worden opgeslagen maar maakt de vraagstelling er niet gemakkelijker op. We kunnen dit probleem oplossen door gebruik te maken van de mogelijkheid om een tabel twee ALIAS namen te geven en die tabellen vervolgens gewoon als twee aparte tabellen te behandelen. Vervolgens doen we precies hetzelfde als in het voorgaande geval; we koppelen de werknemers aan hun chefs, controleren of hun salaris hoger is dan dat van die chef en laten van alle werknemers waar dit voor geldt hun naam afdrukken SELECT WERKERS.NAAM WERKERS, WERKNEMERS CHEFS WHERE WERKERS.CHEF = CHEFS.CODE AND WERKERS.SAL > CHEFS.SAL 4 8

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN Omdat de kolom NAAM in beide tabellen voorkomt moeten we weer aangeven welke kolom we bedoelen. In dit geval is het natuurlijk wel van belang welke van de twee we kiezen! Dit levert ons dan het volgende overzicht op NAAM JANSMA PAREE ZEEUW OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS, WERK, PROJECTEN en SCHALEN tabellen 1. De namen van alle werknemers,die meer verdienen dan iemand anders die in dezelfde woonplaats woont als zij zelf. 2. De namen van alle projecten die een budget hebben dat hoger is dan het budget van een project dat in dezelfde projectplaats wordt uitgevoerd. 3. De namen van alle werknemers die meer verdienen dan werknemer PAREE. 4. De namen van alle werknemers die meer verdienen dan een werknemer die voor dezelfde chef werkt. 5. De namen van alle werknemers die eerder in dienst gekomen zijn dan hun chef. 4 9

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN De NON-EQUIJOIN In de vorige paragraaf hebben we eigenlijk al kunnen zien dat de voorwaarden in het WHERE gedeelte bij de JOIN niet beperkt hoeven te blijven tot gelijkstellende voorwaarden (de EQUIJOIN) maar dat we ook <, > en andere operatoren mogen gebruiken. Zo n JOIN noemen we dan ook een NON-EQUIJOIN. Het is zelfs toegestaan om bijvoorbeeld de BETWEEN operator te gebruiken. Dit zouden we in onze database toe kunnen passen door te vragen naar de namen van de werknemers met daarbij afgedrukt hun salaris en hun salarisschaal. We moeten dan een JOIN formuleren van de WERKNEMERS tabel met de SCHALEN tabel. De SCHAAL die we dan willen koppelen aan de rijen uit de WERKNEMERS tabel betreft dan steeds die schaal die een boven- en een ondergrens kent waartussen het salaris van de desbetreffende werknemer zich bevindt. We kunnen dat als volgt bereiken:,sal,schaal,schalen WHERE SAL BETWEEN LAAGSTE AND HOOGSTE De SUBQUERY Naast het gebruik van de JOIN om gegevens uit meerdere tabellen te raadplegen kennen we de SUBQUERY oftewel de sub-vraag om bepaalde problemen op te lossen. Er is vrijwel geen enkel probleem te bedenken dat we niet ook met de JOIN op zouden kunnen lossen maar in veel gevallen is de SUBQUERY een stuk helderder en bovendien eenvoudiger te formuleren. Stel dat we op zoek zijn naar de namen van alle werknemers die in dezelfde plaats wonen als werknemer BOS. Natuurlijk kunnen we het antwoord hierop in twee stappen verkrijgen: SELECT WOONPLAATS WHERE NAAM = 'BOS' Met als resultaat WOONPLAATS ROTTERDAM en vervolgens WHERE WOONPLAATS = 'ROTTERDAM' Uiteraard levert dit het antwoord op onze vraag op, maar het zou zoveel eenvoudiger zijn als we dat in één stap zouden kunnen doen. Laten we het eerst eens op gaan lossen met een JOIN SELECT WERKERS1.NAAM WERKERS1,WERKNEMERS WERKERS2 WHERE WERKERS2.NAAM = 'BOS' AND WERKERS1.WOONPLAATS = WERKERS2WOONPLAATS 5 0

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN Daarmee koppelen we iedere rij uit de WERKNEMERS tabel (die we WERKERS1 noemen) met iedere rij uit nogmaals de WERKNEMERS tabel (nu WERKERS2 genoemd) als geldt dat in het tweede gedeelte bij NAAM (WERKERS2.NAAM dus) 'BOS' is ingevuld en als bovendien de WOONPLAATS in beide gedeelten (WERKERS1 en WERKERS2) hetzelfde is. Alleen al het feit dat hier enige uitleg nodig is bij de oplossing van een vrij eenvoudige vraag, geeft aan dat de wijze van oplossen niet erg simpel is. Met een SUBQUERY is het eigenlijk veel eenvoudiger op te lossen WHERE WOONPLAATS = ( SELECT WOONPLAATS WHERE NAAM = 'BOS' ) Als we deze oplossing zien, zien we al vrij snel wat de bedoeling is, namelijk 'Geef de namen van alle werknemers met als woonplaats (de woonplaats van BOS)'. Dit is niet alleen wat sneller te doorzien maar sluit ook meer aan bij de wijze waarop we de vraag geformuleerd hadden. Door gebruik te maken van een sub-vraag kunnen we eigenlijk op vrij eenvoudige wijze vraagstellingen zoals in het voorgaande oplossen. WHERE WOONPLAATS =( SELECT WOONPLAATS WHERE CHEF = '40' ) zal dit tot een foutmelding leiden omdat de SUBQUERY hier meerdere woonplaatsen oplevert en geen enkele waarde uit een rij kan gelijk zijn aan de waarden uit meerdere andere rijen. We gebruiken dan de (al eerder behandelde) IN operator. Dus WHERE WOONPLAATS IN ( SELECT WOONPLAATS WHERE CHEF = '40' ) Het is ook mogelijk om meerdere SUBQUERIES aan elkaar te koppelen. De voorgaande vraag zouden we ook kunnen formuleren als: Welke werknemers wonen in dezelfde plaats als de werknemers die als chef MERK hebben? 5 1

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN Dat lossen we op door : WHERE WOONPLAATS IN ( SELECT WOONPLAATS WHERE CHEF IN ( SELECT CODE WHERE NAAM = 'MERK' ) ) OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS, WERK, PROJECTEN en SCHALEN tabellen 1. De namen van werknemers die dezelfde chef hebben als werknemer 40. 2. De namen van werknemers die aan hetzelfde project werken als werknemer 35. 3. De namen van projecten die uitgevoerd worden In dezelfde plaats als de woonplaats van werknemer BOS. 4. De namen van werknemers die in een andere plaats wonen dan werknemer 99. 5. De namen van werknemers die een hoger salaris hebben dan werknemer 12. 6. De namen van projecten waar werknemer MERK aan werkt. 7. De woonplaatsen van werknemers die aan hetzelfde project werken als werknemer MERK. 8. De namen van alle werknemers die in dezelfde salarisschaal vallen als werknemer BOS. 9. De projectcodes van alle projecten waar een werknemer meer dan 30 uur aan gewerkt heeft. 10. De namen van werknemers die dezelfde chef hebben als werknemer MAAS en in dezelfde plaats wonen als werknemer PAREE. 5 2

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN ANY en ALL Speciaal voor gebruik met SUBQUERIES zijn de operatoren ANY en ALL beschikbaar. Hoewel ze in veel gevallen overbodig zijn en vaak vervangen kunnen worden door de (nog te behandelen) EXISTS operator, kunnen in sommige gevallen de ANY en ALL operatoren goede diensten bewijzen. Als we ons bijvoorbeeld afvragen welke werknemers er meer verdienen dan alle werknemers uit ROTTERDAM kunnen we daarvoor de ALL operator in combinatie met de > operator gebruiken WHERE SAL > ALL ( SELECT SAL WHERE WOONPLAATS = 'ROTTERDAM' ) Het gebruik van de ALL operator houdt dus in dat het salaris in alle gevallen groter moet zijn dan de salarissen van werknemers uit ROTTERDAM. Vinden we het daarentegen voldoende als de werknemer een salaris heeft dat hoger is dan dat van een willekeurige werknemer uit ROTTERDAM, dan kunnen we ANY gebruiken WHERE SAL > ANY ( SELECT SAL WHERE WOONPLAATS = 'ROTTERDAM' ) De ANY operator geeft dus aan, dat het voldoen aan één enkele waarde in de SUBQUERY genoeg is. Uiteraard zijn ANY en ALL ook te gebruiken in combinatie met de operatoren >= < en <= Het gebruik van de ANY en ALL operatoren in combinatie met = en! = is wel toegestaan maar niet aan te raden omdat het vaak leidt tot ondoorzichtig geformuleerde vraagstellingen. Bovendien kan dit altijd vermeden worden door gebruik te maken van IN en NOT IN. OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS, WERK, PROJECTEN en SCHALEN tabellen 1. De namen van werknemers die later in dienst zijn gekomen dan alle werknemers uit ROTTERDAM. 2. De namen van werknemers die eerder in dienst zijn gekomen dan een werknemer uit AMSTERDAM. 3. De namen van projecten die een "groter budget hebben dan alle projecten waar werknemer 4Q aan werkt. 5 3

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN 5 4

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN De gesynchroniseerde SUBQUERY Tot nu toe hebben we het uitsluitend gehad over SUBQUERIES die onafhankelijk van de hoofdquery werden uitgevoerd. Met andere woorden, er werd eerst een soort deelverzameling van rijen geselecteerd door middel van een SELECT instructie tussen haakjes om vervolgens de te selecteren rijen te toetsen aan deze verzameling. De samengestelde deelverzameling was dus een statisch geheel waaraan de te selecteren rijen getoetst werden. Het is echter ook mogelijk om de SUBQUERY te koppelen aan de HOOFDQUERY. We kunnen dus voor iedere rij die beoordeeld wordt, opnieuw de deelverzameling waaraan getoetst wordt samen laten stellen. Hierdoor krijgt de subquery een veel dynamischer karakter en loopt dus.eigenlijk synchroon met de hoofdquery, vandaar ook de benaming gesynchroniseerde subquery (soms ook wel gecorreleerde subquery genoemd vanwege het verband (de correlatie) met de hoofdquery). In zijn kernvorm ziet de gesynchroniseerde SUBQUERY er als volgt uit SELECT <kolom naam> FROM <tabel naam 1 > <aliasnaam> WHERE <voorwaarde> ( SELECT <kolomnaam> FROM <tabelnaam2> WHERE <uitdrukking> <operator> <aliasnaam.kolomnaam> ) We hebben een dergelijke constructie bijvoorbeeld nodig als we op zoek zijn naar alle werknemers met een hoger salaris dan het gemiddelde salaris van de werknemers uit hun WOONPLAATS. We hebben dan immers behoefte aan een steeds veranderend gemiddelde salaris namelijk het gemiddelde salaris van de werknemers uit de WOONPLAATS van de werknemer die op dat moment voor selectie in aanmerking komt. Een steeds weer opnieuw te berekenen, dynamisch oftewel gesynchroniseerd gemiddelde! We lossen dit probleem als volgt op W WHERE SAL > ( SELECT AVG(SAL) WHERE WOONPLAATS = W.WOONPLAATS ) Met behulp van een dergelijke constructie zouden we dus ook de vraag kunnen beantwoorden welke werknemer PER WOONPLAATS het hoogste salaris heeft W WHERE SAL = ( SELECT MAX(SAL) WHERE WOONPLAATS = W.WOONPLAATS 5 5

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN ) De EXISTS operator De EXISTS operator wordt gebruikt om te testen of een rij wel of niet voorkomt in een bepaalde tabel. Hoewel de EXISTS operator slechts in enkele gevallen noodzakelijk is, zullen we hem hier toch behandelen. In sommige RDBMS-en is het niet toegestaan om een GROUP FUNCTIE aan beide zijden van een operator te gebruiken. In deze gevallen moeten we de EXISTS operator gebruiken. We zouden ons bijvoorbeeld af kunnen vragen welke werknemers tenminste één of meer werknemers 'onder zich hebben' in de hiërarchie van de organisatie. We zouden dat als volgt op kunnen lossen : W WHERE EXISTS ( SELECT * WHERE CHEF = W.CODE ) Hierbij vragen we ons dus steeds af: 'Bestaat er een rij in de WERKNEMERS tabel waarvoor geldt dat er in de kolom CHEF een waarde is ingevuld die gelijk is aan de waarde die ingevuld is in de kolom CODE van de rij die op het punt staat geselecteerd te worden? Is dat het geval druk dan de waarde uit de kolom NAAM van die rij af.' De oplettende en fantasierijke lezer had natuurlijk al opgemerkt dat dit probleem ook op te lossen is door middel van een JOIN van de tabel WERKNEMERS met zichzelf (de AUTOJOIN dus) SELECT DISTINCT CHEFS.NAAM WERKERS,WERKNEMERS CHEFS WHERE WERKERS.CHEF=CHEFS.CODE OPDRACHTEN Geef een opdracht in SQL voor het opvragen van de volgende overzichten uit de WERKNEMERS, WERK, PROJECTEN en SCHALEN tabellen 1. De namen van alle werknemers die tenminste aan alle projecten werken waar ook werknemer 40 aan werkt. (Gebruik de EXISTS operator!) 5 6

Hoofdstuk: HET RAADPLEGEN VAN MEERDERE TABELLEN UNION, MINUS en INTERSECT De UNION, MINUS en INTERSECT operatoren voeren bewerkingen uit op tabellen die het resultaat zijn van twee SELECT opdrachten. Deze resultaat tabellen kunnen we beschouwen als verzamelingen van elementen. Als we uitgaan van twee verzamelingen A en B, zal de UNION de optelsom van alle elementen van verzameling A en B opleveren. De MINUS geeft bij A - B alle elementen van A behalve die elementen die ook onderdeel uitmaken van B en B - A levert alle elementen van verzameling B op behalve die elementen die ook lid zijn van A. De INTERSECT tenslotte zal alle elementen die voorkomen bij A en ook onderdeel uitmaken van B presenteren. We kunnen dit grafisch duidelijk weergeven: Omdat we hierbij de resultaat tabellen als verzamelingen beschouwen, zullen ook de eventueel dubbel voorkomende rijen buiten beschouwing worden gelaten. Het gebruik van DISTINCT bij deze operatoren is dus overbodig. De UNION operator doet dus eigenlijk niets anders dan twee of meer resultaattabellen bij elkaar tellen. Het spreekt voor zich dat, dat betekent dat het aantal kolommen en het type van de kolommen uit de resultaattabellen met elkaar moeten overeenkomen. We zouden bijvoorbeeld de namen en telefoonnummers van alle werknemers uit AMSTERDAM of UTRECHT in een tabel kunnen zetten met,telefoon WHERE WOONPLAATS = 'AMSTERDAM' UNION,TELEFOON WHERE WOONPLAATS = 'UTRECHT' Deze vraag is natuurlijk gemakkelijker te beantwoorden door gebruik te maken van OR,TELEFOON WHERE WOONPLAATS = 'AMSTERDAM' OR WOONPLAATS = 'UTRECHT' Of met de IN operator,telefoon WHERE WOONPLAATS IN ('AMSTERDAM':UTRECHT') We kunnen echter niet in alle gevallen gebruik maken van IN of OR. 5 7