Holland Casino Eredivisie



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

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

Competitie 1e elftallen, programma t/m Eredivisie

oefeningen eredivisie antwoorden

Speelronde 1. Speelronde 2. Speelronde 3. Vrijdag 6 augustus 2010 Roda JC FC Twente

Query SQL Boekje. Fredrik Hamer

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

Data Definition Language

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

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

Elfde-Liniestraat Hasselt Schooljaar TINFO POKER GAME Oracle Scripts

Sparse columns in SQL server 2008

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

[TOETS SQL INLEIDING]

Structured Query Language (SQL)

SQL Aantekeningen 3. Maarten de Rijke 22 mei 2003

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

Databases en SQL Foundation (DBSQLF.NL)

VERVOLG PROGRAMMA BETAALD VOETBAL SEIZOEN 2012/'13

11. Het selecteren van gegevens deel II

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

Informatie & Databases

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

dins. 29 / woensdag 30 juli 2014 Feyenoord donderdag 31 juli 2014 PSV / FC Groningen JCS zondag 3 augustus 2014 PEC Zwolle Ajax 18:00

G o o g l e. Hoe maak je een eerlijke tabel voor de eredivisie? Bernd Souvignier Radboud Universiteit Nijmegen

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

SQL.

Programma Eredivisie

CONCEPT PROGRAMMA BETAALD VOETBAL SEIZOEN 2014/'15

CONCEPT PROGRAMMA BETAALD VOETBAL SEIZOEN 2014/'15

8. De invoer van gegevens

SQL datadefinitietaal

PROGRAMMA BETAALD VOETBAL SEIZOEN 2015/'16 EREDIVISIE / JUPILER LEAGUE

CONCEPT PROGRAMMA BETAALD VOETBAL SEIZOEN 2015/'16 EREDIVISIE / JUPILER LEAGUE

CONCEPT PROGRAMMA BETAALD VOETBAL SEIZOEN 2013/'14 EREDIVISIE / JUPILER LEAGUE. Zeist, KNVB wedstrijdorganisatie BV, pagina 1 van 14

CONCEPT PROGRAMMA EREDIVISIE / JUPILER LEAGUE SEIZOEN 2012/'13

Zeist, KNVB wedstrijdorganisatie BV, pagina 1 van 15

6. Het maken van een database

PROGRAMMA EREDIVISIE / JUPILER LEAGUE SEIZOEN 2012/'13

Databases - Inleiding

Programma Eredivisie

JCS zondag 31 juli 2016 Feyenoord PSV 18:00

3 Formules. 8 x 6 = x 3 = 12. r-w-w b-w-w g-w-w r-w-r b-w-r g-w-r r-z-w b-z-w g-z-w r-z-r b-z-r g-z-r 6 x 7 = x 100 = 500.

Koppeling met een database

Van CaseTalk naar een database in SQLite studio

SQL & Relationele datamodellen in interactieve media

SQL & Datamodelleren

1 Delers 1. 3 Grootste gemene deler en kleinste gemene veelvoud 12

Programma Eredivisie

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

Programma Eredivisie

We moeten de accommodaties selecteren die 3 sterren hebben, en in land met ID 10 zitten.

Introductie (relationele) databases

Les 11 : Basis SQL (deel2).

Les S-02: Meer geavanceerde SQL-instructies

VOETBAL QUIZ 2015 WIE IS DE VOETBALKENNER VAN VV BRUCHTERVELD?

SQL / Systeemanalyse

Integriteitsbewaking bij een relationele database

Databases SQL - meerdere tabellen

Hoofdstuk: 1 Principes van databases

VBA voor doe het Zelvers deel 22. Handleiding van Helpmij.nl. Auteur: leofact

Les 2 Eenvoudige queries

7. Het selecteren van gegevens

Hoofdstuk 13: Sorteren & Filteren* 2010

Concept programma Eredivisie

Bijlage Inlezen nieuwe tarieven per verzekeraar

Nacompetitie VVV v FC Den Bosch MVV v FC Den Bosch Cambuur v FC Den Bosch

PROGRAMMA BETAALD VOETBAL EREDIVISIE SEIZOEN 2013/'14

Programma Eredivisie

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

ExpertHandboek Business Intelligence met Power BI in Excel Wim de Groot

Competitieprogramma 2006/'07

Zelftest SQL Workshop

Handleiding RoosterGenerator

Structured Query Language

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

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

EXIN Databases en SQL Foundation

Rabobank Schoolvoetbaltoernooi 2010

Data Warehouse Script Generator Doel

NHibernate als ORM oplossing

Excel Controller. Handleiding Excel Controller Wizard

9 oktober 2006 Toestemming voor een commercieel televisieprogramma voor bijzondere omroep. Uw kenmerk Ons Kenmerk Contactpersoon Doorkiesnummer

Zelftest SQL Workshop

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

KONINKLIJKE NEDERLANDSE VOETBALBOND BETAALD VOETBAL. Reglement play-off promotie/degradatie betaald voetbal seizoen 2019/ 20

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

Beste S.D.O.L. -lid & -Supporter,

handleiding S(impel)QL bladzijde 1 Inhoudsopgave

EREDIVISIE. = herfstkampioen

Versieperikelen. Bijlage C

Handleiding configuratie en gebruik tekenmodule

Lab Webdesign: Javascript 3 maart 2008

3. Structuren in de taal

Na bestudering van dit hoofdstuk, moet je tot het volgende in staat zijn:

ECLI:NL:GHAMS:2011:674 Gerechtshof Amsterdam Datum uitspraak Datum publicatie Zaaknummer

Promotie- en degradatieregeling betaald voetbal seizoen 2015/ 16

Straffen in het betaald voetbal tot en met 8 december 2017

Op de werkbalk staan drie knoppen, die van links naar rechts staan voor de drie genoemde stappen.

EREDIVISIE. = herfstkampioen

Transcriptie:

Holland Casino Eredivisie bladzijde 1 Holland Casino Eredivisie 1 De formulering van het probleem Om aan te tonen welke ongekende mogelijkheden de Jet Engine biedt en om S(impel)QL aan een grondige test te onderwerpen is geprobeerd een database te maken, bedoeld om de voetbaluitslagen in op te nemen zoals de KNVB die op haar website publiceert. Daarbij is uitgegaan van de Holland Casino Eredivisie van het voetbalseizoen 2004/05 waarbij de wedstrijduitslagen verwerkt zijn tot en met 10 februari 2005. Niet omdat dit zomaar een datum is, maar omdat de stand op die datum nog een punt van zorg openbaart omdat er iets bijzonders aan de hand is. De stand van de competitie op 10 februari volgens de KNVB Elftal G W GL V Punten DPV DPT 1 PSV 20 16 3 1 51 54 12 2 AZ 20 15 4 1 49 49 14 3 Ajax 20 12 5 3 41 44 19 4 Feyenoord 20 10 5 5 35 50 27 5 Vitesse 20 10 3 7 33 34 27 6 FC Utrecht 20 9 5 6 32 25 21 7 SC Heerenveen 20 9 5 6 32 34 33 8 Roda JC 20 8 5 7 29 36 31 9 RKC Waalwijk 20 7 6 7 27 26 27 10 NAC 20 7 6 7 27 28 40 11 FC Twente 20 7 4 9 25 24 26 12 FC Groningen 20 7 4 9 25 28 36 13 Willem II 20 7 4 9 25 25 33 14 NEC 20 4 7 9 19 22 28 15 ADO Den Haag 20 5 4 11 19 23 31 16 RBC Roosendaal 20 3 2 15 11 21 51 17 De Graafschap 20 2 5 13 11 21 51 18 FC Den Bosch 20 2 3 15 9 11 48 We willen deze stand zelf kunnen maken aan de hand van de wekelijkse uitslagen van gespeelde wedstrijden. Die uitslagen weer is de bron de KNVB zijn in de volgende vorm gegoten:

Holland Casino Eredivisie bladzijde 2 Datum Thuis Uit Uitslag 06.02.05 RKC Waalwijk - 1 FC Groningen - 1 3-0 06.02.05 FC Utrecht - 1 NAC - 1 2-0 06.02.05 Ajax - 1 FC Twente - 1 1-2 06.02.05 Vitesse - 1 Roda JC - 1 3-0 05.02.05 SC Heerenveen - 1 AZ - 1 1-3 05.02.05 ADO Den Haag - 1 FC Den Bosch - 1 2-0 04.02.05 Willem II - 1 NEC - 1 3-1 04.02.05 De Graafschap - 1 Feyenoord - 1 2-7 De tabel is niet zo geschikt als hij op het eerste gezicht lijkt. Het gebruikte datumformat is voor sql ongeschikt en de laatste kolom is bij voorkeur in tweeën te splitsen. Bovendien kunnen we de aanduiding dat het om het eerste elftal gaat wel missen. Het omzetten van de oorspronkelijk gepubliceerde tabel naar een voor ons doel geschikt format is een kwestie van ETL (extraction, transformation en loading). Laten we ons daarmee hier onze hersens niet pijnigen. We doen net alsof de data van de gespeelde wedstrijden aangeleverd worden als: datum thuis uit dpv dpt 06-02-05 RKC Waalwijk FC Groningen 3 0 06-02-05 FC Utrecht NAC 2 0 06-02-05 Ajax FC Twente 1 2 06-02-05 Vitesse Roda JC 3 0 05-02-05 SC Heerenveen AZ 1 3 05-02-05 ADO Den Haag FC Den Bosch 2 0 04-02-05 Willem II NEC 3 1 04-02-05 De Graafschap Feyenoord 2 7 02-02-05 PSV RBC Roosendaal 4 1 30-01-05 NEC FC Utrecht 0 0... De opdracht die we ons in deze kleine en simpel ogende casus stellen is tweeledig. We willen een database realiseren die alle valkuilen tot inconsistentie van gegevens vermijdt en op een willekeurige datum de stand van de competitie kunnen produceren Alle uitslagen tot en met 10 februari staan in het script wedstrijd.sql. Voordat we dat script op onze database los kunnen laten moeten we eerst de database maken. Dat is met behulp van S(impel)Ql een peulenschil. We besluiten de database naar de Holland Casino Eredivisie te vernoemen en gaan vervolgens denken over een zodanig veilig ontwerp dat aan het eerste aspect van de opdracht voldaan is. Daarna proberen we ook het tweede deel van de opdracht te realiseren waarbij we de regel hanteren dat elke gewonnen wedstrijd drie wedstrijdpunten en elke gelijkgespeelde wedstrijd één wedstrijdpunt oplevert. Verloren wedstrijden dragen niet bij aan het totaal.

Holland Casino Eredivisie bladzijde 3 2 De bedrijfsregels op een rijtje Het lijkt zo simpel; maar om straks fouten bij invoer van de rest van de gespeelde wedstrijden te voorkomen is meer nodig dan op het eerste gezicht vermoed kan worden. Er schuilen behoorlijk wat addertjes onder het gras. We beginnen met het formuleren van een aantal bedrijfsregels die de KNVB hanteert en die we om in de komende paragrafen gemakkelijk naar deze regels te kunnen verwijzen van een nummering voorzien hebben. B.1 De speeldata moeten binnen het seizoen vallen. Dat wil voor het seizoen 2004/05 zeggen tussen 13-08-2004 en 22-05-2005. B.2 Alleen clubs uit de eredivisie mogen als thuis- of uitploeg worden opgenomen. B.3 Een club speelt maar één keer tegen elke tegenstander een thuiswedstrijd. B.4 Een club speelt maar één keer tegen elke tegenstander een uitwedstrijd (overbodige regel die volledig wordt afgedekt door de vorige!). B.5 Een club kan op één dag slechts één wedstrijd spelen. B.6 Voor het aantal doelpunten (zowel voor als tegen) mag nooit een negatief getal genoteerd worden. B.7 Alle gegevens zijn noodzakelijk; er mag geen enkel gegeven ontbreken. Nou was laatst op de radio te vernemen dat Feyenoord hoofdzakelijk tegen zichzelf liep te voetballen... Misschien moet je zo'n opmerking niet al te letterlijk nemen maar hij roept wel de vraag op of we niet als bedrijfsregel moeten formuleren dat de thuisclub een andere moet zijn dan de uitclub. Met andere woorden: moeten we niet uitsluiten dat de thuisspelende club dezelfde is als de uitspelende? Als we de situatie wat grondiger bekijken blijkt een dergelijke regel overbodig te zijn. De regel dat een club maar één wedstrijd per dag kan spelen laat zich splitsen in een drietal op het eerste gezicht onnodig ingewikkeld geformuleerde regels. B.5.1 Een club speelt per dag hooguit één keer thuis. B.5.2 Een club speelt per dag hoogstens één keer uit. B.5.3 Een club speelt op dezelfde dag nooit zowel een thuis- als een uitwedstrijd. En B.5.3 sluit nou precies een wedstrijd FC Groningen tegen FC Groningen uit. We gaan er gemakshalve aan voorbij dat er zich bijzondere situaties kunnen voordoen zoals wedstrijden die onderbroken worden en op een andere dag worden uitgespeeld of het bij wijze van strafmaatregel drie wedstrijdpunten in mindering brengen. Dergelijke situaties vragen om ad hoc oplossingen waar we hier geen boodschap aan hebben. Het zal blijken dat zonder al die bijzonderheden het probleem al groot genoeg is.

Holland Casino Eredivisie bladzijde 4 3 Alle begin is (niet) moeilijk Het idee van een tabel begint met een kolomindeling waarbij we de kolommen een naam en een type toebedelen. Bovendien moeten we een naam voor de tabel bedenken. De keuze is gevallen op de tabelnaam wedstrijd terwijl de kolomnamen datum, thuis, uit, dpv en dpt voor de hand liggen na lezing van het voorafgaande. Als kolomtypen kiezen we in dezelfde volgorde DATE, TEXT(20), TEXT(20), INTEGER en INTEGER. En vervolgens dienen de beperkingsregels of constraints zich aan waarmee we de vastgestelde bedrijfsregels moeten afdekken. We hebben achtereenvolgens in sql de beschikking over constraints in de vorm van de PRIMARY KEY, de FOREIGN KEY, de NOT NULL-, de UNIQUE- en de CHECK-constraint. Met deze vijf zullen we de klus moeten klaren. Gewapend met de in jaren opgebouwde kennis komen we al snel tot een constructie waarmee aan het merendeel van de bedrijfregels is voldaan CREATE TABLE wedstrijd (datum DATE NOT NULL, thuis TEXT(20), uit TEXT(20), dpv INTEGER NOT NULL, dpt INTEGER NOT NULL, PRIMARY KEY (thuis, uit), UNIQUE (datum, thuis), UNIQUE (datum, uit), CHECK (dpt >= 0), CHECK (dpv >= 0), CHECK (datum BETWEEN #13-08-04# AND #22-05-05#)); script1.sql Ga als u het effect van dit script wilt proberen na of u de datumconversie van S(impel)QL gebruikt. Omdat de achtste van de dertiende en de vijfde van de tweeëntwintigste toevallig niet geldig zijn zal Jet SQL aan deze data de juiste draai geven. Maar dat geldt zeker niet voor alle andere in dit datumformaat genoteerde data. De bedrijfsregels waaraan door dit script tegemoet gekomen wordt zijn B.1, B.3, B.4, B5.1, B.5.2, B.6 en B.7. Blijven B.2 en B.5.3 over.

Holland Casino Eredivisie bladzijde 5 4 Een kleine aanpassing Nog een opmerking over de eerste bedrijfsregel. Als we in de wedstrijdentabel een wedstrijddatum invullen moet die volgens het scriptje hierboven liggen tussen de twee seizoenuitersten. Dat betekent dat we in maart alvast de uitslagen van april en mei kunnen invoeren. En dat is onzinnig. Dus proberen we het uit te sluiten door als bovengrens de systeemdatum op te nemen. We kunnen nog wel de wedstrijdtabel met terugwerkende kracht bijwerken maar geen voorschot op toekomstige wedstrijduitslagen nemen. Om de systeemdatum te kunnen gebruiken moeten we een klein uitstapje maken naar de VBA-functies waar Jet SQL uitstekend mee overweg kan. S(impel)QL trouwens ook, wat alleen al te zien is aan de manier waarop de syntax herkend wordt. Om gesjoemel met wedstrijden zoveel mogelijk tegen te gaan is een kleine aanpassing van het script nodig waarbij we de bovengrens van het seizoen vervangen door de huidige datum. VBA kent hiervoor de functie DATE(). DROP TABLE wedstrijd; CREATE TABLE wedstrijd (datum DATE NOT NULL, thuis TEXT(20), uit TEXT(20), dpv INTEGER NOT NULL, dpt INTEGER NOT NULL, PRIMARY KEY (thuis, uit), UNIQUE (datum, thuis), UNIQUE (datum, uit), CHECK (dpt >= 0), CHECK (dpv >= 0), CHECK (datum BETWEEN #13-08-04# AND DATE())); script2.sql Om het geheel te kunnen testen is een script ontworpen om de wedstrijdtabel te vullen. In het script (zie volgende bladzijde) wordt gepoogd alle bedrijfsregels met voeten te treden. Slechts de eerste twee wedstijden verdienen te worden geaccepteerd; het zijn immers de echte uitslagen van onlangs gespeelde voetbalpartijtjes. Als we de zaak aan een test onderwerpen blijkt er nog een aantal onmogelijke wedstrijden door de mazen van de constraints te glippen. Daar gaan we in de komende paragrafen iets aan doen maar eerst volgt een minimale aanpassing waar we straks de vruchten van plukken. Behalve van de VBA functie DATE() ontmoeten we in dit artikel nog twee van dergelijke functies in de vorm van CSTR(<arg>) en IIF(<voorwaarde>, <arg1>,<arg2>). CSTR (convert to string) promoveert zowat elk argument tot een tekst. Of we nu een datum of een getal als argument vermelden, het resultaat is steeds een weergave van het argument in tekstuele vorm. De functie IIF (immediate if) kennen we ongetwijfeld van MS Excel en heeft als functieresultaat de waarde van arg1 of arg2, afhankelijk of de voorwaarde bevestigend of ontkennend beantwoord wordt.

Holland Casino Eredivisie bladzijde 6 DELETE * FROM wedstrijd; (#29-01-05#, 'FC Twente', 'Feyenoord', 0, 0); -- correct (#23-01-05#, 'Feyenoord', 'Vitesse', 1, 2); -- correct (#16-05-02#, 'AZ', 'Ajax', 0, 0); -- B.1 (#16-05-05#, 'FC Heerenveen', 'PSV', 3, 0); -- B.1 DATE() (#23-08-05#, 'FC Twente', 'AZ', 1, 1); -- B.1 (#11-11-04#, 'Sparta', 'AZ', 2, 1); -- B.2 (#14-10-04#, 'FC Twente', 'Vittese', 1, 1); -- B.2 (#03-02-05#, 'FC Twente', 'Feyenoord', 1, 5); -- B.3, B4 (#29-01-05#, 'PSV', 'FC Twente', 1, 2); -- B.5.1 (#29-01-05#, 'Feyenoord', 'PSV', 1, 0); -- B.5.2 (#23-11-04#, 'NAC', 'NAC', 0, 1); -- B.5.1, B.5.2 (#13-12-04#, 'Roda JC', 'RKC Waalwijk', 3, -1); -- B.6 (#11-09-04#, 'FC Heerenveen', 'NEC', -6, 0); -- B.6 (#10-10-04#, 'AZ', 'Willem II', 3, NULL); -- B.7 (#06-09-04#, 'Roda JC', 'NAC', NULL, 1); -- B.7 (#02-12-04#, 'De Graafschap', NULL, 3, 1); -- B.7 (#18-08-04#, NULL, 'Feyenoord', 3, 1); -- B.7 (NULL, 'ADO Den Haag', 'RBC Roosendaal', 1, 0); -- B.7 script3.sql We zijn al een heel eind maar het moeilijkste deel moet nog komen!

Holland Casino Eredivisie bladzijde 7 5 Foutmeldingen Bij de test die we natuurlijk met behulp van script3 uitvoeren komen een paar rijen ten onrechte in de wedstrijdentabel terecht. Veel meer tot tevredenheid stemt het dat de meeste door het script voorgestelde rijen als gevolg van de constraints die al zijn aangebracht worden geweerd uit de database. Minder fraai is de manier waarop we als gebruiker hiervan op de hoogte worden gesteld. Niet iedereen is in staat zinnen als "Een of meer waarden zijn strijdig met de validatieregel Check_2658B1D7_1DDC_4AD5 die is ingesteld voor wedstrijd." te waarderen. Het lezen laat staan begrijpen van hexadecimale aanduidingen is niet voor iedereen weggelegd. Wel aardig van het systeem is dat de wetmatigheid die we overtreden blijkbaar een CHECK-constraint is (maar dat zagen we ook al aan de veel vriendelijker aandoende term validatieregel). We kunnen voor constraints die we meegeven zelf een naam bedenken waarbij we waarschijnlijk nooit Check_2658B1D7_1DDC_4AD5 zullen kiezen. Het hoeft niet, maar foutmeldingen worden begrijpelijker. Bovendien en daarmee lopen we een klein beetje vooruit op wat nog gaat komen hebben we die naam soms echt nodig en moeten we hem zelfs intikken. Zelf een naam geven kan met het woordje CONSTRAINT, gevolgd door een zelf gekozen aanduiding. Het aangepaste script laat zien hoe dat moet. DROP TABLE wedstrijd; CREATE TABLE wedstrijd (datum DATE NOT NULL, thuis TEXT(20), uit TEXT(20), dpv INTEGER NOT NULL, dpt INTEGER NOT NULL, CONSTRAINT c1 PRIMARY KEY (thuis, uit), CONSTRAINT c2 UNIQUE (datum, thuis), CONSTRAINT c3 UNIQUE (datum, uit), CONSTRAINT c4 CHECK (dpv >= 0), CONSTRAINT c5 CHECK (dpt >= 0), CONSTRAINT c6 CHECK (datum BETWEEN #13-08-04# AND DATE())); script4.sql Opnieuw script3 uitvoeren levert al heel wat vriendelijker foutmeldingen. Eventueel kunnen ook de NOT NULL-constraints van een naam voorzien worden. Verboden is het niet maar de naam zal zelden nodig zijn en het script wordt er niet leesbaarder op.

Holland Casino Eredivisie bladzijde 8 6 Referentiële integriteit We keren terug naar het eigenlijke probleem waar nog twee bedrijfsregels openstaan. Bedrijfsregel B2 is op zich niet moeilijk en laat zich naar keuze verwezenlijken door een CHECK-constraint of een FOREIGN KEY. In beide gevallen hebben we een extra tabel nodig waarin de namen van de elftallen staan die in de wedstrijdtabel mogen voorkomen. We noemen deze tabel eredivisie en laten hem precies uit één kolom club bestaan waarvoor we het type TEXT(20) kiezen. De enige kolom doet tevens dienst als sleutel (die hebben we nodig om in de wedstrijdentabel een FOREIGN KEY te definieren). Als we de keuze hebben tussen een FOREIGN KEY en een CHECK-constraint gaat de voorkeur sterk uit naar de eerste. Een foreign key is nou eenmaal als index gedefinieerd en een check-constraint niet. En juist die index zorgt voor de nodige snelheid. Omdat we de uitslagentabel laten verwijzen naar de eredivisieclubs moeten we als eerste de tabel met de namen van die clubs creëren. We hebben de tabel erg summier gehouden. De structuur van de tabel is dan ook allesbehalve ingewikkeld te noemen. CREATE TABLE eredivisie (club TEXT(20), PRIMARY KEY (club)); Met eenvoudige toevoegqueries laat de tabel zich vullen. script5a.sql INSERT INTO eredivisie VALUES('ADO Den Haag'); INSERT INTO eredivisie VALUES('Ajax');... script5b.sql Voor de overige zestien eredivisieclubs zijn overeenkomstige regels te bedenken. Om tikwerk te voorkomen is in een combinatie van beide scripts in de vorm van script5.sql voorzien. Nu terug naar de wedstrijdentabel en daarin met twee foreign keys afdwingen dat het om clubs uit de eredivisie gaat. Waarom we die foreign keys ON UPDATE CASCADE (trapsgewijs bijwerken) hebben gedefinieerd leggen we later uit. En waarschijnlijk ziet u ook niet waarom we de rijen van de eredivisietabel gedurende het lopende seizoen zouden willen bijwerken...

Holland Casino Eredivisie bladzijde 9 DROP TABLE wedstrijd; CREATE TABLE wedstrijd (datum DATE NOT NULL, thuis TEXT(20), uit TEXT(20), dpv INTEGER NOT NULL, dpt INTEGER NOT NULL, CONSTRAINT c1 PRIMARY KEY (thuis, uit), CONSTRAINT c2 UNIQUE (datum, thuis), CONSTRAINT c3 UNIQUE (datum, uit), CONSTRAINT c4 CHECK (dpv >= 0), CONSTRAINT c5 CHECK (dpt >= 0), CONSTRAINT c6 CHECK (datum BETWEEN #13-08-04# AND DATE()), CONSTRAINT c7 FOREIGN KEY (thuis) REFERENCES eredivisie(club) ON UPDATE CASCADE, CONSTRAINT c8 FOREIGN KEY (uit) REFERENCES eredivisie(club) ON UPDATE CASCADE); script6.sql Natuurlijk probeert u weer of de onmogelijke uitslagen van script3 geaccepteerd worden. Het zijn inderdaad minder foute uitslagen dan voorheen: de wedstrijden van Sparta en Vittese zijn geweigerd! We schieten al een heel eind op maar zitten nog steeds met de vijfde bedrijfsregel in onze maag. We zijn nog steeds in staat een club als Feyenoord twee keer op dezelfde dag te laten voetballen.

Holland Casino Eredivisie bladzijde 10 7 Vervolmaking Natuurlijk hebben we het moeilijkste tot het laatst bewaard. Maar misschien valt het wel mee. In Jet SQL is het mogelijk om check-constraints gegevens op allerlei manieren te laten valideren. Bij andere database management systemen zijn dit soort constraints vaak minder flexibel en mag bijvoorbeeld de validatie alleen betrekking hebben op de gegevens in de rij die op dat moment bewerkt wordt. Op die manier hebben we ook de constraints c4, c5 en c6 ingezet. Op het moment van validatie zijn alleen gegevens uit de rij zelf betrokken. Jet SQL kan echter op dat moment ook andere gegevens raadplegen. Uit de tabel zelf waarin de validatieregel een rol vervult, maar ook uit andere tabellen of zelfs verenigingen van tabellen (joins). We hebben daar in de vorige paragraaf al een beetje op gezinspeeld toen we beweerden dat de referentiële integriteit ook met een check-constraint kan worden afgewongen. Hoe dat in zijn werk gaat ziet u in het volgende fragment waarbij we weer de wedstrijdtabel als voorbeeld nemen.... CONSTRAINT c7 CHECK (thuis IN (SELECT club FROM eredivisie)),... Er is hier sprake van een subquery waarbij de constructie eigenlijk hetzelfde is als bij gebruik van een subquery in de where clause van een normale selectquery. De haakjes (één paar voor de subquery en één paar voor de check-constraint) vertroebelen de zaak een beetje. Maar daar is overheen te komen. Het wordt moeilijker als de subquery een selectie betreft op de tabel die we nou net aan het creëren zijn. Als de validatie uitsluitend op één kolom betrekking heeft valt nog wel iets te bereiken. We worden gedwongen onze toevlucht tot een list te zoeken als dat niet het geval is. In onze wedstrijdentabel proberen we te voorkomen dat we een club een thuiswedstrijd laten spelen op dezelfde datum waarop die club ook al een uitwedstrijd heeft afgewerkt. (In de definitieve versie moeten we ook voorkomen dat een club een uitwedstrijd krijgt toebedeeld als op dezelfde datum al een thuiswedstrijd gespeeld is!) Intuïtief (iedereen heeft klompen) voelen we aan dat we een subquery krijgen die zoiets op moet leveren als SELECT uit, datum FROM wedstrijd; en dat dan de combinatie (thuis, datum) daarin niet voor mag komen. Twee kolommen, dus moeten we een list verzinnen. We maken van twee kolommen één. Klein probleem daarbij is wel dat de kolommen uit en datum van een verschillend type zijn. Maar daar hebben we de oplossing al voor aangestipt. We passen een VBA-functie toe om de twee kolommen aaneen te smeden.

Holland Casino Eredivisie bladzijde 11 Script7 bevat de twee benodigde constraints de we voor de afwisseling a1 en a2 gedoopt hebben. DROP TABLE wedstrijd; CREATE TABLE wedstrijd (datum DATE NOT NULL, thuis TEXT(20), uit TEXT(20), dpv INTEGER NOT NULL, dpt INTEGER NOT NULL, CONSTRAINT c1 PRIMARY KEY (thuis, uit), CONSTRAINT c2 UNIQUE (datum, thuis), CONSTRAINT c3 UNIQUE (datum, uit), CONSTRAINT c4 CHECK (dpv >= 0), CONSTRAINT c5 CHECK (dpt >= 0), CONSTRAINT c6 CHECK (datum BETWEEN #13-08-04# AND DATE()), CONSTRAINT c7 FOREIGN KEY (thuis) REFERENCES eredivisie(club) ON UPDATE CASCADE, CONSTRAINT c8 FOREIGN KEY (uit) REFERENCES eredivisie(club) ON UPDATE CASCADE, CONSTRAINT a1 CHECK (thuis + CSTR(datum) NOT IN (SELECT uit + CSTR(datum) FROM wedstrijd)), CONSTRAINT a2 CHECK (uit + CSTR(datum) NOT IN (SELECT thuis + CSTR(datum) FROM wedstrijd))); script7.sql Eindelijk lijkt het erop dat we het eerste deel van de opdracht die we onszelf stelden naar tevredenheid hebben afgerond. Nogmaals uitproberen van script2 levert perfect de lijst van de twee geoorloofde wedstrijden op. Ons systeem (of eigenlijk systeempje) houdt zich keurig aan alle bedrijfsregels.

Holland Casino Eredivisie bladzijde 12 8 Tenslotte We hebben nog twee kleine verrassingen voor u in petto. Of liever: het systeem eentje en wij eentje. U wist al dat u de tabel eredivisie pas te verwijderen is als eerst de tabel wedstrijd overboord gegooid is? Dat komt, omdat zolang als de tabel wedstrijd bestaat, de tabel eredivisie aanwezig moet zijn vanwege de in wedstrijd bestaande foreign keys. Maar probeert u met DROP TABLE wedstrijd; de tabel wedstrijd te verwijderen, zal database op een tamelijk kryptische manier hiertegen protesteren. "De DDL kan in deze tabel niet worden uitgevoerd, omdat er door voorwaarde van tabel naar wordt verwezen" De tabel wedstrijd heeft de tabel wedstrijd nodig en dus moet eerst de tabel wedstrijd verwijderd worden vóórdat de tabel wedstrijd verwijderd kan worden. Een verhaal van de kip en het ei, dat alleen is op te lossen door eerst de beperkingen die door de constraints a1 en a2 zijn opgelegd weg te nemen met ALTER TABLE wedstrijd DROP CONSTRAINT a1; ALTER TABLE wedstrijd DROP CONSTRAINT a2; In het voorlopig definitieve script (knvb1.sql) hebben we deze regels opgenomen. Dat brengt ons bij de tweede verrassing. U hebt zich ongetwijfeld afgevraagd waarom we bij de definitie van de twee foreign keys zonodig ON UPDATE CASCADE moesten toevoegen. Met de foreign keys voorkomen we dat we uitslagen allerlei clubs als Sparta, Emmen of zelfs FC Stormvogels aan de wedstrijdentabel kunnen toevoegen. Zelfs tikfouten als Feijenoord of Ajaks worden afgestraft. Maar wat we niet kunnen voorkomen zijn schrijffouten als FEYenoord en ajax. In het onderstaande script (script8.sql) is opzettelijk een groot aantal van dit soort fouten geslopen.

Holland Casino Eredivisie bladzijde 13 DELETE FROM wedstrijd; (#06-02-05#, 'RKC WaAlwiJK', 'fc groningen', 3, 0); (#06-02-05#, 'fc UtRecht', 'Nac', 2, 0); (#06-02-05#, 'AjaX', 'fc TwENte', 1, 2); (#06-02-05#, 'ViTESse', 'roda jc', 3, 0); (#30-01-05#, 'ajax', 'ADO Den Haag', 0, 0); (#30-01-05#, 'NAC', 'SC heerenveen', 1, 1); (#30-01-05#, 'RKC Waalwijk', 'psv', 1, 4); (#29-01-05#, 'Vitesse', 'RBC roosendaal', 1, 3);... script8.sql Het resultaat van dit script het zijn allemaal uitslagen uit de werkelijkheid is ronduit bedroevend: Ondanks alle voorzorgsmaatregelen zal dit overzicht nauwelijks een schoonheidsprijs verdienen. De Jet Engine is niet erg gevoelig voor wat betreft het gebruik van hoofdletters en kleine letters. Natuurlijk is het zo, dat niemand het in zijn hoofd haalt zoveel vergissingen te maken al we in script8 begaan hebben. Maar door te overdrijven wordt het effect van een op het eerste gezicht nauwelijks iets uithalend sql-statement misschien nog duidelijker. We doelen hier op:

Holland Casino Eredivisie bladzijde 14 UPDATE eredivisie SET club = club; Voer de opdracht uit en bekijk daarna de lijst van uitslagen in de wedstrijdentabel nog maar een keer. U zult verbaasd zijn... Het complete script om de database te creëren staat in knvb1.sql. Zowel de eredivisie- als wedstrijdentabel worden gemaakt en gevuld met respectievelijk de achttien clubs en de inmiddels 180 gespeelde wedstrijden. In het tweede deel van dit artikel zoeken we een antwoord op de vraag of en vooral op welke manier het mogelijk is stand van de competitie aan de hand van deze wedstrijden op te vragen.

Holland Casino Eredivisie bladzijde 15 9 Winst, gelijkspel of verlies We beginnen onze zoektocht door de wedstrijden met een eenvoudig experiment waarbij we voor de thuisspelende clubs vier kolommen genereren (het worden er straks wel meer). De kolommen die deze query oplevert voorzien we van de opschriften club, g (van gewonnen), gl (van gelijkgespeeld) en v (van verloren). De functie IIF is eerder uitgelegd. SELECT thuis AS club, IIF(dpv > dpt, 1, 0) AS w, IIF(dpv = dpt, 1, 0) AS gl, IIF(dpv < dpt, 1, 0) AS v FROM wedstrijd; Het resultaat is niet zo heel erg overrompelend al gaat het in totaal om 180 rijen. Het resultaat van de bovenste rij is dat van de wedstrijd tussen RKC Waalwijk en FC Groningen (gewonnen door de Waalwijkers). Logisch dat die winstpartij voor RKC Waalwijk een 1 oplevert in de kolom w en een 0 in de kolommen gl en v. Maar dan moet FC Groningen als gevolg van deze wedstrijd ergens de combinatie 0, 0, 1 toebedeeld krijgen, wat niet het geval is althans niet als gevolg van het bedoelde voetbaltreffen. FC Groningen was daarbij de uitclub en die hebben we in de selectie niet meegenomen. Een paar kleine aanpassingen in de formulering van de query zijn voldoende om de resultaten van de uitspelende clubs te laten zien. SELECT uit AS club, IIF(dpv < dpt, 1, 0) AS w, IIF(dpv = dpt, 1, 0) AS gl, IIF(dpv > dpt, 1, 0) AS v FROM wedstrijd; U kunt natuurlijk ook in plaats van de groter-dan- en kleiner- dan-tekens te verwisselen een paar nullen omruilen tegen enen en omgekeerd. De letters w en v verwisselen is ook een mogelijkheid, maar dan komt de volgorde van de kolommen van de selectie voor de thuisspelende clubs niet meer overeen met die van de selectie voor de uitspelende elftallen.

Holland Casino Eredivisie bladzijde 16 Beide queries breiden we uit met het aantal doelpunten dat het betreffende elftal scoorde en om zijn oren kreeg. Met het oog op de komende paragrafen knopen we ook de wedstrijddatum eraan vast. De queries worden dan: SELECT thuis AS club, IIF(dpv > dpt, 1, 0) AS w, IIF(dpv = dpt, 1, 0) AS gl, IIF(dpv < dpt, 1, 0) AS v, dpv, dpt, datum FROM wedstrijd; en script11.sql SELECT uit AS club, IIF(dpv < dpt, 1, 0) AS w, IIF(dpv = dpt, 1, 0) AS gl, IIF(dpv > dpt, 1, 0) AS v, dpt, dpv, datum FROM wedstrijd; script12.sql Nee, het is geen vergissing dat de oorspronkelijk kolommen dpt en dpv in de tweede query in omgekeerde volgorde staan. De kolom dpv van de tabel wedstrijd bevat nou eenmaal de tegendoelpunten van de uitspelende club terwijl het aantal goals dat het bezoekende elftal maakte in de kolom dpt van de wedstrijdtabel te lezen valt... U begrijpt al waar we naar toe willen? Door beide tabellen aan elkaar te plakken komen we al een heel eind. SELECT thuis AS club, IIF(dpv > dpt, 1, 0) AS w, IIF(dpv = dpt, 1, 0) AS gl, IIF(dpv < dpt, 1, 0) AS v, dpv, dpt, datum FROM wedstrijd UNION ALL SELECT uit, IIF(dpv < dpt, 1, 0), IIF(dpv = dpt, 1, 0), IIF(dpv > dpt, 1, 0), dpt, dpv, datum FROM wedstrijd; script13.sql Een paar opmerkingen. Gebruik liever geen UNION maar een UNION ALL. De eerste werkt vertragend omdat daarbij de rijen vooraf gesorteerd moeten worden om vervolgens dubbele rijen te negeren. Voor dat laatste hoeven we hier trouwens niet zo bang te zijn. Omdat we bij

Holland Casino Eredivisie bladzijde 17 elke rij de speeldatum vermeld hebben kan eenzelfde rij nooit twee keer voorkomen. En als u niet begrijpt waarom dat zo is, moet u het eerste deel van dit verhaal nog maar eens goed lezen. Een tweede opmerking betreft het achterwege laten van de kolomaliassen in de uitselectie. Die hebben daar geen enkele zin. Een UNION betrekt de kolomopschiften uitsluitend van de eerste query.

Holland Casino Eredivisie bladzijde 18 10 Teleurstelling Het resultaat dat we vonden bij de vorige query (script13.sql) brengt de oplossing van ons probleem een grote stap dichterbij. Vooral, als we ook nog eens de datum aan het resultaat vastknopen. Het ligt voor de hand om het resultaat als view op te slaan om op die view nieuwe selecties te laten plaatsvinden. CREATE VIEW hulptabel AS SELECT thuis AS club, IIF(dpv > dpt, 1, 0) AS w, IIF(dpv = dpt, 1, 0) AS gl, IIF(dpv < dpt, 1, 0) AS v, dpv, dpt, datum FROM wedstrijd UNION ALL SELECT uit, IIF(dpv < dpt, 1, 0), IIF(dpv = dpt, 1, 0), IIF(dpv > dpt, 1, 0), dpt, dpv, datum FROM wedstrijd; script14.sql Wat bij de Jet Engine de volgende reactie tweegbrengt: "Samenvoegingen zijn niet toegestaan in een subquery" Met andere woorden: het idee van de union kan naar de prullenbak. Een beetje database management systeem draait daar echt zijn hand niet voor om. U begrijpt nu waarschijnlijk het kopje van deze paragraaf!

Holland Casino Eredivisie bladzijde 19 11 De ontdekking van een wondermiddel Jet SQL is amper gedocumenteerd. Veel moet dan ook door experimenteren worden uitgedokterd. Eén van die vage begrippen uit Jet SQL is het fenomeen procedure, niet te verwarren met de term stored procedure die in de databasewereld een vertrouwde klank heeft. Een procedure blijkt van alles te kunnen zijn. Hij accepteert willekeurige queries dus niet alleen selectiequeries zoals een view. Hij slikt parameters (parametergebruik in een view is niet toegestaan). Een procedure vindt het prima als er iets over de volgorde van rijen gezegd wordt (kom in een view niet aanzetten met een ORDER BY). En hij verwerkt ook nog eens UNIONS (iets dat nou net bij een view onmogelijk gebleken is). Er zijn nog veel meer verschillen maar dit betoog heeft een ander doel dan het bieden van een cursus Jet SQL. Omdat de meesten niet vertrouwd zijn met het begrip procedure geven we een voorbeeld hoe we zo'n alleskunner kunen maken. We doen dat aan de hand van de misschien wel merkwaardigste query die we tot nu toe in dit hele verhaal hebben uitgevoerd en die we gebruikten om rare schrijfwijzen te herstellen. We doelen op UPDATE eredivisie SET club = club; Deze query laat zich eenvoudig tot procedure promoveren. CREATE PROCEDURE corrigeer AS UPDATE eredivisie SET club = club; script15.sql Vanaf nu hoeft u de correctiequery nooit meer in te tikken maar kunt u volstaan met EXECUTE corrigeer; U mag EXECUTE bovendien verbasteren tot EXEC. Handig?

Holland Casino Eredivisie bladzijde 20 Misschien verbaast de volgende query u nu niet meer: CREATE PROCEDURE hulptabel AS SELECT thuis AS club, IIF(dpv > dpt, 1, 0) AS w, IIF(dpv = dpt, 1, 0) AS gl, IIF(dpv < dpt, 1, 0) AS v, dpv, dpt, datum FROM wedstrijd UNION ALL SELECT uit, IIF(dpv < dpt, 1, 0), IIF(dpv = dpt, 1, 0), IIF(dpv > dpt, 1, 0), dpt, dpv, datum FROM wedstrijd; script16.sql Script16 is een kleine variant op script14. Alleen het woordje VIEW is vervangen door PROCEDURE. We krijgen de inhoud van de hulptabel niet alleen te zien met EXECUTE hulptabel maar ook SELECT * FROM hulptabel; openbaart het resultaat van onze eerdere hersenspinsels. Een procedure die een resultaat in tabelvorm oplevert kan gewoon als tabel aangesproken worden. We kunnen onze ogen nauwelijks geloven, maar de volgede query slaagt echt:

Holland Casino Eredivisie bladzijde 21 CREATE VIEW mosterd_na_de_maaltijd AS SELECT * FROM hulptabel; Nu we de smaak van procedures te pakken hebben is onze behoefte aan bovenstaande instructie niet meer zo groot.

Holland Casino Eredivisie bladzijde 22 12 De stand We zijn nog maar één procedure verwijderd van het moment waarop we de competitiestand in beeld kunnen brengen. We zouden met een view ook een heel eind komen maar dan moeten we omdat volgorde een wezenlijk kenmerk van de tabel op de allereerste bladzijde is een of andere vorm van groeperen toepassen (GROUP BY). Het is immers niet toegestaan ORDER BY in de definitie van zo'n view op te nemen. Bij een procedure mag dat wel (we verbazen ons nergens meer over). Kijken we naar de bedoelde stand dan ontwaren we afgezien van de kolom die de plaats in de stand aangeeft achtereenvolgens de naam van het elftal, het aantal gespeelde wedstrijden, het aantal gewonnen wedstrijden, het aantal gelijkgespeelde wedstrijden, het aantal verloren westrijden, het totaal aan wedstrijdpunten, het totaal aan zelf gemaakte doelpunten en het totaal aan tegengoals. Dat is in SQL niet zo moeilijk voor elkaar te krijgen aan de hand van onze hulptabel. SELECT club, COUNT(*), SUM(w), SUM(gl), SUM(v), SUM(3 * w + gl), SUM(dpv), SUM(dpt) FROM hulptabel GROUP BY club;

Holland Casino Eredivisie bladzijde 23 Vóórdat we de vorige query tot procedure een view komt voor dit werk ook in aanmerking verheffen geven we nog een kolomalias mee zodat de tabel 'leesbaarder' wordt. Bovendien is er nog wel iets over de volgorde op te merken al zullen de supporters van ADO Den Haag ingenomen zijn met de bovenste plaats van de ranglijst. SELECT club, COUNT(*) AS g, SUM(w) AS w, SUM(gl) AS gl, SUM(v) AS v, SUM(3 * w + gl) AS punten, SUM(dpv) AS dpv, SUM(dpt) AS dpt FROM hulptabel GROUP BY club ORDER BY SUM(3 * w + gl) DESC, SUM(dpt) - SUM(dpv); script17.sql In de in script17 geformuleerde query wordt de volgorde bepaald door het aantal westrijdpunten en bij gelijk aantal wedstrijdpunten wordt ook nog eens gerangschikt op doelsaldo (de omgekeerde volgorde van het totale aantal doelpunten vóór verminderd met het totale aantal tegendoelpunten). Vergelijken met de echte stand (zie de eerste bladzijde) brengt een klein verschil aan het licht tussen de plaats van Willem II en FC Groningen. Beide ploegen hebben evenveel wedstrijdpunten en een gelijk doelsaldo. Hetzelfde fenomeen valt waar te nemen bij RBC Roosendaal en De Graafschap. Welke regel de KNVB in zo'n geval hanteert is niet geheel duidelijk maar als het daarbij gaat om welk van de twee de meeste doelpunten scoorde is dat eenvoudig in script17 te verwerken. Gewoon door als derde sorteercriterium dit aantal te vermelden (SUM(dpv) DESC), ofschoon dit ons bij het koppel RBC Roosendaal - De Graafschap geen snars verder brengt. Van de query in scipt17 kan geen view gemaakt worden vanwege de opgegeven sortering. Een view accepteert nu eenmaal geen ORDER BY. We nemen onze toevlucht tot een procedure. CREATE PROCEDURE stand AS SELECT club, COUNT(*) AS g, SUM(w) AS w, SUM(gl) AS gl, SUM(v) AS v, SUM(3 * w + gl) AS punten, SUM(dpv) AS dpv, SUM(dpt) AS dpt FROM hulptabel GROUP BY club ORDER BY SUM(3 * w + gl) DESC, SUM(dpt) - SUM(dpv); stand.sql

Holland Casino Eredivisie bladzijde 24 Vanaf nu kunnen we als we de wedstrijdentabel bijgewerkt hebben op elk moment de stand van de competitie opvragen met EXECUTE stand; of met SELECT * FROM stand; En ofschoon we toch echt het object stand als procedure gedefinieerd hebben herkent de Jet Engine deze procedure als view. Vraag de metadata maar op. Terwijl we heus geprobeerd hebben stand als view te definiëren en toen met de mededeling afgescheept werden dat hetgeen we wilden allemaal te ingewikkeld was met de letterlijke boodschap Alleen eenvoudige SELECT-query's zijn toegestaan in VIEWS

Holland Casino Eredivisie bladzijde 25 13 Op elke gewenste datum? We hebben nog niet helemaal aan het door ons gestelde doel beantwoord. We wilden immers de stand van de competitie op elke gewenste datum kunnen zien. In de wetenschap dat we bij procedures parameters kunnen verwerken is dit niet zo moeilijk. Pas nu blijkt waarom we de wedstrijddatums in onze hulptabel hebben meegenomen. Tot dusverr hebben we hier nog geen gebruik van gemaakt. CREATE PROCEDURE stand_per AS SELECT club, COUNT(*) AS g, SUM(w) AS w, SUM(gl) AS gl, SUM(v) AS v, SUM(3 * w + gl) AS punten, SUM(dpv) AS dpv, SUM(dpt) AS dpt FROM hulptabel WHERE datum <= x GROUP BY club ORDER BY SUM(3 * w + gl) DESC, SUM(dpt) - SUM(dpv); stand_per.sql In de definitie van de procedure is één regel toegevoegd waarmee alle wedstrijden van na een bepaalde datum (hier x, maar elke naam is natuurlijk goed) worden uitgesloten. Hoe u de waarde van x opgeeft blijkt uit de volgende formulering waarin geïnformeerd wordt naar de stand op 3 september: EXECUTE stand_per #03-09-2004#; Het enige wat nu nog ontbreekt zijn de plaatsnummers als eerste kolom van de tabel. Ook daar valt nog wel wat aan te doen. De manier waarop we dat voor elkaar gekregen hebben vereist enige inzicht in het begrip self join.

Holland Casino Eredivisie bladzijde 26 14 Finishing touch De plaatsnummers zijn een probleem apart. We gaan ervan er gemakshalve vanuit dat twee clubs die evenveel punten en een even hoog doelsaldo hebben gelijkgeplaatst zijn. Op 10 februari 2005 onze testdatum is dat maar liefst twee keer het geval. Zowel FC Groningen en Willem II als RBC Roosendaal en De Graafschap bezetten dezelfde plaats in de rangorde (tenzij we andere criteria erbij betrekken). We volstaan met het aanreiken van de oplossing met de kanttekening dat we als uitgangspunt gebruikt hebben dat in de vaderlandse competitie het doelsaldo altijd kleiner zal zijn dan duizend keer het aantal wedstrijdpunten (behalve in het geval het laatste nul is). Bij twijfel verhoogt u de vermenigingsvuldigingfactor... Een vleugje inner join in combinatie met union all doet de rest. Je moet alleen op het idee komen. CREATE PROCEDURE stand2_per AS SELECT count(*) + 1 AS nr, a.club, a.g, a.w, a.gl, a.v, a.punten, a.dpv, a.dpt FROM WHERE stand_per a, stand_per b 1000 * a.punten + a.dpv - a.dpt < 1000 * b.punten + b.dpv - b.dpt GROUP BY a.club, a.g, a.w, a.gl, a.v, a.punten, a.dpv, a.dpt UNION ALL SELECT 1, club, g, w, gl, v, punten, dpv, dpt FROM stand_per WHERE 1000 * punten + dpv - dpt = (SELECT MAX(1000 * punten + dpv - dpt) FROM stand_per) ORDER BY nr; stand2_per.sql Onderstaand overzicht toont het resultaat van EXECUTE stand2_per #10-02-05#; Nog niet overtuigd van de mogelijkheden van Jet SQL? Kijk dan maar eens naar de posities van FC Groningen, Willem II, RBC Roosendaal en De Graafschap!

Holland Casino Eredivisie bladzijde 27 Voor de oplettende lezer: het is een beetje zonde om de procedure stand_per in bovenstaande definitie te gebruiken. Daarin is immers voorzien in een volgordebepaling die we in stand2_per overschrijven. Maar om stand_per zonder volgordebepaling te herschrijven vonden we net iets teveel werk. En bent u slechts geïnteresseerd in de stand na alle wedstrijden vervangt u stand_per gewoon door stand. En als iemand opmerkt dat het misschien mooier is om de stand te laten zien als

Holland Casino Eredivisie bladzijde 28 laten we het helemaal aan hem- of haarzelf over om de oplossing te vinden. Wij vinden het zo wel welletjes. Wat we gerealiseerd hebben is een robuuste database met twee tabellen die alle regels van de KNVB respecteert. En passant is een procedure ontwikkeld die onvolkomenheden in de manier waarop namen van elftallen geschreven kunnen worden op een letterlijk verfrissende wijze corrigeert. Daarnaast is de database voorzien van een aantal procedures waarmee we de stand van de competitie op elk gewenst moment kunnen reproduceren. Hopelijk is met dit voorbeeld voldoende aangetoond dat Jet SQL misschien meer respect verdient dan het soms krijgt. Laat deze casus in ieder geval een uitdaging zijn voor ontwikkelaars die zweren bij andere database management systemen. Leiden, Toon Kuipers