Structured Query Language

Marc Andries voor Acces databases.
Aangepast voor het Web door Lieven Smits

Inhoud


Inleiding (*)

Een Database Management System (DBMS) moet een tekstuele taal bevatten waarmee je tabellen kan creëren en gegevens opvragen. De meest gebruikte taal bij relationele DBMS (RDBMS) is SQL . In deze tekst volgt een beschrijving van een aantal commando's uit de Access variant van SQL.

De database management talen bevatten twee soorten commando's, die meestal statements worden genoemd:

Een SQL statement bevat een aantal sleutelwoorden, en begint steeds met een sleutelwoord. In het algemeen zal men de sleutelwoorden in hoofdletters vermelden en de niet-sleutelwoorden zoals tabel- en veldnamen niet. Hiertoe bestaat echter geen verplichting. Je kan verschillende regels gebruiken om een commando te formuleren. In Access SQL (en vele andere varianten) moet je SQL statements van elkaar scheiden met een kommapunt (;). Standaard wordt elke Access SQL statement met een ; beëindigd, ook wanneer het statement op zichzelf staat.

Een SQL statement wordt opgesplitst in een aantal clauses, die elk met een sleutelwoord beginnen.

In de hiernavolgende syntaxis van de statements worden optionele elementen tussen [ ] vermeld. Alternatieve elementen worden tussen { } vermeld en van elkaar gescheiden door een | . Een herhaling wordt aangeduid door drie ...

Het veruit belangrijkste statement is het SELECT statement, dat behoort tot de DML en waarmee je gegevens kan opvragen. Wanneer je in Access een Query opstelt, kan je dit in een Query By Example venster doen in het Query Design View. Je kan echter ook de query intypen in het SQL View. Welke werkwijze je ook gebuikt, steeds wordt door Access automatisch de andere versie geproduceerd. Wanneer je een SQL query bewaart, gebeurt dit steeds in zijn tekstuele SQL gedaante.

1. De SQL Data Definition Language (DDL)

In de Data Definition Language (DDL) kan je onder meer nieuwe tabellen creëren, de structuur van bestaande tabellen wijzigen, en tabellen verwijderen.

In Access wordt de SQL DDL weinig gebruikt omdat de QBE (Query By Example) methode gebruiksvriendelijker en uitgebreider is. Daarom worden hierna slechts beknopt een aantal commando's met hun betekenis en een voorbeeld vermeld.

1.1 CREATE TABLE

Je creëert een nieuwe tabel in Access met de New knop in het Tables tabblad van het Database venster.

In SQL gebruik je het statement

CREATE TABLE tablename
   (fieldname datatype [(fieldsize)]
    [CONSTRAINT name {PRIMARY KEY | UNIQUE | REFERENCES tablename}]
    [, ...]
    [, CONSTRAINT name
       {PRIMARY KEY (fieldname [, ...]) | UNIQUE (fieldname [, ...])
        | FOREIGN KEY (fieldname [, ...]) REFERENCES tablename} [, ...]]);

De SQL notaties die overeenkomen met de Access datatypes, verschillen nogal van de SQL/92 standaard:

Data type (field size) (QBE)

datatype (SQL - Access)

datatype (SQL/92 standaard)

datatype (mySQL)

Text

TEXT
(of CHAR, CHARACTER)

CHARACTER [VARYING]

VARCHAR

Memo

LONGTEXT (of MEMO)

-

Text

Number (Byte)

Number (Integer)

Number (Long Integer)

Number (Single)

Number (Double)

BYTE

SHORT (of SMALLINT)

LONG (of INT, INTEGER)

SINGLE (of REAL)

DOUBLE (of FLOAT)

-

SMALLINT

INTEGER

REAL

DOUBLE PRECISION, FLOAT

TINYINT

INT

BIGINT

FLOAT

DOUBLE

Date/Time

DATETIME (of DATE, TIME, TIMESTAMP)

DATE, TIME, TIMESTAMP

DATE, TIME, TIMESTAMP

Currency

CURRENCY

-

DECIMAL(n,2)

AutoNumber (Long Integer)

COUNTER

-

INT

Yes/No

BOOLEAN
(of LOGICAL, YESNO)

- BOOLEAN

Je kan één enkele primaire sleutel definiëren met de CONSTRAINT name PRIMARY KEY clause. Wanneer je één enkel veld als primaire sleutel wilt definiëren, plaats je CONSTRAINT name PRIMARY KEY onmiddellijk achter de veldnaam. Wanneer je meerdere velden als primaire sleutel wilt definiëren, plaats je CONSTRAINT name PRIMARY KEY (fieldname [, ...]) na de laatste veldnaam en gescheiden van deze veldnaam door een komma.

Het sleutelwoord UNIQUE komt overeen met de Indexed (Yes, No Duplicates) field property en kan meermaals voorkomen.

Je kan vreemde sleutels definiëren met de REFERENCES clause, die meermaals kan voorkomen. Na REFERENCES wordt de naam van de tabel vermeld waarmee een relatie wordt gelegd. Wanneer je één enkel veld als vreemde sleutel wilt definiëren, plaats je CONSTRAINT name REFERENCES tablename onmiddellijk achter de veldnaam. Wanneer je meerdere velden als vreemde sleutel wilt definiëren, plaats je CONSTRAINT name FOREIGN KEY (fieldname [, ...]) REFERENCES tablename na de laatste veldnaam en gescheiden van deze veldnaam door een komma.

In de SQL/86 standaard kan je in het CREATE TABLE statement geen primaire of vreemde sleutel definiëren , maar je kan wel aanduiden dat een veld uniek moet zijn (met het sleutelwoord UNIQUE). In de SQL/92 standaard kan je een primaire of vreemde sleutel definiëren op een analoge wijze als in Access 97, maar het woord CONSTRAINT en de name vallen weg en de definitie gebeurt steeds op het einde van het CREATE TABLE statement

Voorbeeld:

De creatie van de tabel Studenten gebeurt in SQL als volgt:

CREATE TABLE Studenten
    (studentnr LONG CONSTRAINT PrimaryKey PRIMARY KEY,
     achternaam TEXT(25),
     voornaam TEXT(15),
     adres TEXT(30),
     postcode TEXT(4),
     woonplaats TEXT(30),
     telefoon TEXT(13),
     geb_datum DATE,
     geslacht TEXT(5),
     vooropleiding LONGTEXT,
     jaar SMALLINT,
     klas TEXT(4));

1.2 ALTER TABLE

Je kan de structuur van een bestaande tabel in Access wijzigen met de Design knop in het Tables tabblad van het Database venster.

In SQL gebruik je het statement

ALTER TABLE tablename
 {ADD COLUMN fieldname datatype [(fieldsize)]
   [CONSTRAINT name {PRIMARY KEY | UNIQUE | REFERENCES tablename}]
 | ADD CONSTRAINT name
   {PRIMARY KEY (fieldname [, ...]) | UNIQUE (fieldname [, ...])
    | FOREIGN KEY (fieldname [, ...]) REFERENCES tablename}
 | DROP COLUMN fieldname
 | DROP CONSTRAINT name};

Voorbeeld:

Het leggen van een relatie tussen twee tabellen gebeurt in SQL door een veld (of meerdere velden) als een vreemde sleutel te definiëren en aan te geven naar welke (primaire sleutel van een) tabel wordt verwezen. Je legt als volgt een relatie tussen de tabellen Stagiaires en Studenten:

ALTER TABLE Stagiaires
    ADD CONSTRAINT studentnr
        FOREIGN KEY (studentnr) REFERENCES Studenten;

Hierbij wordt aangenomen dat studentnr reeds vooraf als primaire sleutel voor de tabel Studenten werd gedefinieerd.

1.3 DROP TABLE

Je kan een bestaande tabel in Access verwijderen door hem in het Tables tabblad van het Database venster te selecteren en de Delete knop in te drukken.

In SQL gebruik je hiervoor het statement

DROP TABLE tablename;

2. De SQL Data Manipulation Language (DML)

In Access is SQL vooral interessant als alternatieve taal voor QBE om queries op te stellen. Deze queries vallen uiteen in twee categorieën:

  1. Met een Make Table Query maak je een nieuwe tabel door de resultaten van een Select Query als een nieuwe tabel te bewaren. Het equivalente SQL statement heet SELECT INTO
  2. Met een Update Query wijzig je de waarden van één of meer velden in een tabel voor één of meer records. Het equivalente SQL statement heet UPDATE.
  3. Met een Append Query voeg je nieuwe records toe aan een tabel. Het equivalente SQL statement heet INSERT INTO.
  4. Met een Delete Query verwijder je één of meer records uit een tabel. Het equivalente SQL statement heet DELETE.

3. Het SELECT statement (*)

Met het SELECT statement kan je gegevens uit één of meerdere tabellen opvragen.

Hierbij moet je een onderscheid maken tussen het SELECT statement zelf, dat eigenlijk een programma is, en de tabel die als resultaat van het SELECT statement wordt getoond. Deze tabel is virtueel. Dit betekent dat hij niet echt bestaat, maar bij het uitvoeren van het SELECT statement wordt opgesteld en slechts bestaat zolang het bijhorende venster geopend blijft. Wanneer je gegevens in een virtuele tabel tracht te wijzigen, zullen de wijzigingen in werkelijkheid in de onderliggende echte tabellen (tables, ook base tables of reële tabellen genoemd) gebeuren. In vele gevallen is er geen eenduidig verband tussen een rij of kolom uit een virtuele tabel en een rij of kolom uit een reële tabel. In die gevallen zal de virtuele tabel of een deel ervan niet kunnen worden gewijzigd.

In Access wordt een virtuele tabel die het resultaat is van een query een recordset genoemd. Heel vaak kan je hierin de inhoud van sommige of alle rijen of kolommen wijzigen. Dan spreekt Access van een dynaset (dynamische verzameling). Soms kan je geen enkele waarde in de resulterende tabel wijzigen. Dan spreekt Access van een snapshot (momentopname).

Hierbij zijn enkele belangrijke opmerkingen nodig:

Algemeen bevat een SELECT query zes soorten clauses, waarvan de eerste twee steeds voorkomen, en de vier andere kunnen voorkomen:

SELECT fieldlist
FROM tablelist
WHERE condition
GROUP BY group_fieldlist
HAVING group_condition
ORDER BY fieldlist;

3.1 SELECT in één tabel (*)

De eenvoudigste vorm van het SELECT statement haalt de gegevens uit één enkele tabel, die in de FROM clause wordt vermeld.

3.1.1 De SELECT en FROM clauses (*)

De SELECT en FROM clauses zijn steeds de eerste clauses van een SELECT statement.

De syntaxis van de SELECT en FROM clauses is:

SELECT [{ALL | DISTINCT | DISTINCTROW | TOP n [PERCENT]}]
    fieldname [AS alias] [, ...]
    FROM tablename

In zijn eenvoudigste vorm SELECT fieldname [, ...] FROM tablename; worden de aangegeven kolommen uit een tabel getoond in de opgegeven volgorde.

Als fieldname mag * worden vermeld. Dit is een korte notatie voor alle velden uit de tabel, in dezelfde volgorde als in de tabel.

Fieldname hoeft niet noodzakelijk een veld uit een tabel te zijn. Het kan ook een berekend veld zijn zoals prijs*hoeveelheid. In dit laatste geval spreek je beter van een berekende kolom, aangezien deze kolom niet met een veld overeenkomt. De inhoud van een berekende kolom is read-only.

Je kan elke kolom een andere naam geven door na fieldname AS alias te vermelden. De alias geldt dan als alternatieve kolomnaam. Hij wordt als kolomhoofd getoond. Bij dubbele voorkomende kolommen of kolomnamen moet je een alias opgeven. Bij berekende kolommen hoef je geen alias op te geven, maar je doet dit wel best.

Als fieldname kan een zogenaamde aggregate function worden gebruikt, waarbij totalen voor alle records worden berekend, indien er geen GROUP BY clause is, en totalen per groep, indien er een GROUP BY clause is (zie infra). In standaard SQL zijn er vijf aggregate functions (COUNT - aantal, SUM - totaal, AVG - gemiddelde, MAX - grootste waarde, MIN - kleinste waarde). Access SQL heeft daarenboven extra aggregate functions (STDEV - standaardafwijking van de steekproef, VAR - variantie van de steekproef, STDEVP - standaardafwijking van de populatie, VARP - variantie van de populatie). Daarnaast kan je ook berekende totalen opstellen. Per groep wordt één rij getoond. Het resultaat is steeds een snapshot en kan niet worden gewijzigd.

Wanneer een aggregate function wordt gebruikt, moeten alle andere te tonen kolommen

Na het sleutelwoord SELECT kan één van vier sleutelwoorden volgen. Deze hebben de betekenis:

Voorbeelden:

Neem aan dat de tabel Klanten de volgende records en velden bevat:

klantnr (primaire sleutel)

naam

gemeente

1

Janssens

Brussel

2

Janssens

Brussel

3

Peeters

Brussel

De hiernavolgende SELECT statements leveren de erbij vermelde recordsets op:

naam

gemeente

Janssens

Brussel

Janssens

Brussel

Peeters

Brussel

naam

gemeente

Janssens

Brussel

Peeters

Brussel

naam

gemeente

Janssens

Brussel

Janssens

Brussel

Peeters

Brussel

naam

gemeente

Janssens

Brussel

Janssens

Brussel

naam

gemeente

Janssens

Brussel

Janssens

Brussel

In dit laatste geval wordt het getoonde percentage (50% van 3) naar boven afgerond. Bij gelijke waarden voor het ORDER BY veld worden alle gelijk geordende records getoond.

Wanneer de eerste kolom in de tabel ontbreekt, en er dus geen primaire sleutel is, leveren de hiernavolgende SELECT statements de volgende recordsets op:

SELECT ALL naam, gemeente FROM Klanten; (of SELECT naam, gemeente FROM klanten;):

naam

gemeente

Janssens

Brussel

Janssens

Brussel

Peeters

Brussel

SELECT DISTINCT naam, gemeente FROM Klanten; :

naam

gemeente

Janssens

Brussel

Peeters

Brussel

SELECT DISTINCTROW naam, gemeente FROM Klanten; :

naam

gemeente

Janssens

Brussel

Peeters

Brussel

In dit laatste geval worden er slechts twee rijen getoond, omdat alle kolommen in de fieldlist voorkomen, en er twee identieke rijen zijn.

3.1.2 De WHERE clause (*)

De WHERE clause is niet verplicht in een SELECT statement. Ze wordt gebruikt om via voorwaarden een restrictie op te leggen aan de te tonen records of rijen.

De syntaxis van de WHERE clause is:

WHERE condition

Hierbij is condition een uitdrukking waarvan de waarde TRUE of FALSE is. Ze wordt opgebouwd met de logische operatoren AND, OR en NOT en de vergelijkingsoperatoren =, <, >, <=, >= en <>. Daarenboven kunnen ook wildcards (*, ?, #, [ ] en !) en andere operatoren zoals Like, In en Between voorkomen. Het gebruik van ( ) dient om de volgorde van de evaluatie van de operatoren te bepalen.

De Like operator vergelijkt een tekstuitdrukking in het linkerlid, die een wildcard bevat, met een patroon tussen " "in het rechterlid. Een patroon is een veralgemeende tekstuitdrukking, d.w.z. een tekst waarin nog enige vrijheid bestaat. Zo staat "A*" voor alle teksten die met een A beginnen, en staat "?end" voor alle teksten van vier tekens waarbij het eerste teken volledig willekeurig is en de laatste drie letterlijk "end" zijn.

De betekenis van de wildcards is als volgt:

* (Mysql: %) Nul, één of meer willekeurige tekens

? (Mysql: _) Exact één willekeurig teken

# (Mysql: [0-9])Exact één willekeurig cijfer

[ ] (Mysql: []) Een reeks tekens tussen [ ] betekent exact één teken uit de opgegeven reeks. Binnen de [ ] betekent een streepje (-) een bereik van tekens. [A-Za-z] staat dus voor een willekeurige letter. Een uitroepteken betekent een uitsluiting. [!0-9] staat dus voor een willekeurig teken dat geen cijfer is. Wanneer de wildcards als teken worden bedoeld, staan ze tussen [ ].

Voorbeelden (tracht zelf de betekenis uit te zoeken):

SELECT * FROM Studenten
WHERE woonplaats = "Antwerpen";

SELECT * FROM Studenten
WHERE achternaam >="N";

SELECT * FROM Studenten
WHERE achternaam Like [!V]*

SELECT * FROM Studenten
WHERE NOT achternaam Like "V*"

SELECT * FROM Studenten
WHERE postcode Like "[1-3]###"

SELECT * FROM Begeleiders
WHERE vak Like "*[?]*"

SELECT * FROM Studenten
WHERE woonplaats In ("Brussel", "Gent", "Leuven");

SELECT * FROM Studenten
WHERE woonplaats = "Brussel" OR woonplaats = "Gent"
OR woonplaats = "Leuven";

SELECT * FROM Studenten
WHERE woonplaats = "Brussel" OR achternaam Like "V*";

SELECT * FROM Studenten
WHERE woonplaats = "Brussel" AND achternaam Like "V*";

SELECT * FROM Studenten
WHERE geb_datum Between #09/15/72# And #12/31/72#;

SELECT * FROM Studenten
WHERE geb_datum >= #09/15/72# AND geb_datum <= #12/31/72#;

SELECT stagenr, [einde stage] - [begin stage] AS [duur stage]
FROM Studenten
WHERE [begin stage] >="01/01/72#;

3.1.3 De ORDER BY clause (*)

De ORDER BY clause is niet verplicht in een SELECT statement. Ze wordt gebruikt om een ordening op te leggen aan de te tonen records of rijen.

De syntaxis van de ORDER BY clause is:

ORDER BY fieldname [{ASC | DESC}][, ...]

De getoonde rijen worden geordend op de waarden in de kolommen die worden opgegeven in de ORDER BY clause. Indien er meerdere kolommen zijn, wordt eerst geordend op de eerste kolom, daarna op de volgende enz. Standaard wordt stijdend geordend (0-9 en A-Z). Je kan de volgorde van ordenen wijzigen door na het fieldname ASC (stijgend) of DESC (dalend) te vermelden. Als fieldname moeten veldnamen of uitdrukkingen worden gebruikt, maar geen aliassen!

Wanneer de ORDER BY clause ontbreekt, staat de volgorde van de rijen niet vast.

3.1.4 De GROUP BY clause (*)

De GROUP BY clause is niet verplicht in een SELECT statement. Ze wordt gebruikt wanneer via aggregate functions totalen berekend worden voor groepen records i.p.v. voor alle records samen.

De syntaxis van de GROUP BY clause is:

GROUP BY field [, ...]

Een groep bestaat uit alle records met gelijke waarden voor alle velden (of uitdrukkingen) in de GROUP BY clause. Niet in de GROUP BY clause vermelde kolommen die wel in de SELECT clause voorkomen, moeten een aggregate function bevatten.

Voorbeelden:

SELECT Count(*) AS aantal, Avg([einde stage] - [begin stage]) AS [gemiddelde duur] FROM Stages;

SELECT Count(*) AS aantal, Avg([einde stage] - [begin stage]) AS [gemiddelde duur] FROM Stages
GROUP BY instellingsnr;

In de eerste query wordt het aantal stages en de gemiddelde duur van een stage berekend. De laatste formule is een berekend totaal. Er is één enkele rij.

In de tweede query worden dezelfde berekeningen gemaakt, maar dan opgesplitst per instelling. Er is één rij per instelling.

3.1.5 De HAVING clause (*)

De HAVING clause is niet verplicht in een SELECT statement. Ze wordt gebruikt om via voorwaarden een restrictie op te leggen aan groepen. Dit betekent dat ze in principe enkel samen met de GROUP BY clause voorkomt .

De syntaxis van de HAVING clause is:

HAVING condition

Hierbij is condition een uitdrukking waarvan de waarde TRUE of FALSE is. Ze wordt opgebouwd met de logische operatoren AND, OR en NOT en de vergelijkingsoperatoren =, <, >, <=, >= en <>. Daarenboven kunnen wildcards zoals * en ? en andere operatoren zoals Like, In en Between voorkomen. Het gebruik van haakjes ( ) dient om de volgorde van de evaluatie van de operatoren te bepalen.

Het verschil tussen de WHERE en HAVING clauses bestaat er in dat de velden in de condition van de HAVING clause ofwel velden zijn die binnen een groep één waarde aannemen (maar dan kan een WHERE clause worden gebruikt, wat efficiënter is), ofwel aggregate functions bevatten.

Een tweede verschil tussen de WHERE en de HAVING clause betreft het ogenblik waarop de voorwaarde wordt getest. Bij de WHERE clause gebeurt dit voor individuele rijen vóór het groeperen; bij de HAVING clause gebeurt dit voor de groepstotalen na het groeperen.

Aan de hand van enkele voorbeelden wordt hierna het verschil in het gebruik van WHERE en HAVING geïllustreerd. De queries zijn gebaseerd op de volgende tabel Klanten:

naam (primaire sleutel)

geslacht

woonplaats

omzet

Adriaens

vrouw

Brugge

50 000

Bellens

man

Brussel

20 000

De Bock

vrouw

Gent

44 000

De Groot

vrouw

Brussel

36 000

Mortier

man

Kortrijk

15 000

Peeters

man

Brussel

29 000

Rogiers

man

Gent

97 000

Van den Broecke

man

Antwerpen

35 000

Willems

vrouw

Antwerpen

27 000

Wouters

man

Lier

16 000

SELECT woonplaats, Count(*) AS aantal, Sum(omzet) AS [totale omzet] FROM Klanten
WHERE geslacht = "man"
GROUP BY woonplaats
ORDER BY woonplaats;

woonplaats

aantal

totale omzet

Antwerpen

1

35 000

Brussel

2

49 000

Gent

1

97 000

Kortrijk

1

15 000

Lier

1

16 000

SELECT woonplaats, Count(*) AS aantal, Sum(omzet) AS [totale omzet] FROM Klanten
GROUP BY woonplaats
HAVING Count(*)>1
ORDER BY woonplaats;

woonplaats

aantal

totale omzet

Antwerpen

2

62 000

Brussel

3

85 000

Gent

2

141 000

SELECT woonplaats, Count(*) AS aantal, Sum(omzet) AS [totale omzet] FROM Klanten
WHERE woonplaats = "Brussel" OR woonplaats = "Antwerpen"
GROUP BY woonplaats
ORDER BY woonplaats;

SELECT woonplaats, Count(*) AS aantal, Sum(omzet) AS [totale omzet] FROM Klanten
GROUP BY woonplaats
HAVING woonplaats = "Brussel" OR woonplaats = "Antwerpen"
ORDER BY woonplaats;

woonplaats

aantal

totale omzet

Antwerpen

2

62 000

Brussel

3

85 000

SELECT woonplaats, Count(*) AS aantal, Sum(omzet) AS [totale omzet] FROM Klanten
WHERE geslacht = "man"
GROUP BY woonplaats
HAVING Count(*)>1
ORDER BY woonplaats;

woonplaats

aantal

totale omzet

Brussel

2

49 000

3.2 SELECT in meerdere tabellen (*)

Naast queries gebaseerd op één tabel zijn er ook queries gebaseerd op meerdere tabellen. Hierbij worden tabellen samengevoegd. Men spreekt dan van een join tussen tabellen. Er zijn drie grote soorten joins:

Je kan elk van deze joins aanduiden m.b.v. de FROM en de WHERE clause:

Algemene vereenvoudigde syntaxis (de niet relevante clauses worden weggelaten):

SELECT fieldlist
FROM table1, table2
WHERE table1.field1 = table2.field2;

SELECT fieldlist
FROM table1, table2
WHERE table1.field1 T table2.field2
;

SELECT fieldlist
FROM table1, table2;

Hierbij zijn table1 en table2 de gejoinde tabellen. Field1 en field2 zijn de velden uit resp. table1 en table2 die de relatie bepalen. T is een symbool dat een vergelijkingsoperator verschillend van = aanduidt, bijvoorbeeld kleiner dan (<) of groter dan (>) (Gewoonlijk wordt hier de Griekse hoofdletter Theta voor gebruikt, maar op het world wide web kiezen we de gewone letter T).

Algemeen zullen veldnamen in een query gebaseerd op meer dan één tabel worden voorafgegaan door de naam van de tabel waaruit zij afkomstig zijn en een punt. Wanneer er geen dubbelzinnigheden zijn mag je deze tabelnaam (en het punt) echter weglaten.

In Access SQL en SQL/92 kan bij een equi join de join voorwaarde in de WHERE clause worden vermeld, maar zal ze standaard in de FROM clause worden vermeld. Deze heeft dan de gedaante

SELECT fieldlist
FROM table1 [jointype] JOIN table2
ON table1.field1 = table2.field2;

Deze formulering is enkel mogelijk bij een equi join bij een one to many of een one to one relatie. De linkse tabel table1 is dan steeds de tabel aan de one zijde van de one to many relatie en de rechtse tabel table2 de tabel aan de many zijde van de one to many relatie.

Het jointype kan in Access drie waarden aannemen (zie infra).

Wanneer eenmaal tabellen zijn samengevoegd kunnen de WHERE, GROUP BY, HAVING en ORDER BY clauses worden gebruikt zoals bij één tabel queries. In de resulterende dynaset zullen wel sommige gegevens (uit tabellen aan de one zijde van de relatie) herhaald worden getoond, met allerlei gevolgen voor de mogelijkheid tot wijzigen (niet mogelijk of een wijziging in meerdere rijen tegelijk!). Hierop wordt in deze tekst echter niet verder ingegaan.

3.2.1 INNER JOIN (*)

Een inner join bevat enkel records uit beide tabellen waartussen een equi join relatie bestaat. Dit is de standaard join, die wordt aangeduid met de het jointype INNER. Records uit één van beide tabellen die geen corresponderend record hebben in de andere tabel worden niet getoond in de dynaset.

SELECT fieldlist
FROM table1 INNER JOIN table2
ON table1.field1 = table2.field2;

Voorbeeld (one to many relatie Studenten - Stagiaires):

SELECT Stagiaires.studentnr, achternaam, voornaam, stagenr, begeleidersnr
FROM Studenten INNER JOIN Stagiares ON Studenten.studentnr = Stagiaires.studentnr;

Enkel de namen van de studenten die als stagiares stage lopen worden vermeld, met het nummer van hun stage en van hun begeleider.

3.2.2 LEFT JOIN

Een left outer join bevat alle records uit de tabel table1 aan de one zijde van de relatie en enkel die records uit de tabel table2 aan de many zijde van de relatie waarvoor een corresponderend record in de andere (one) tabel table1 bestaat. Deze join wordt aangeduid met het jointype LEFT. De join wordt een left outer join genoemd omdat de tabel aan de one zijde van de relatie steeds links van de sleutelwoorden LEFT JOIN staat.

SELECT fieldlist
FROM table1 LEFT JOIN table2
ON table1.field1 = table2.field2;

Voorbeeld (one to many relatie Studenten - Stagiaires):

SELECT Studenten.studentnr, achternaam, voornaam, stagenr, begeleidersnr
FROM Studenten INNER JOIN Stagiares ON Studenten.studentnr = Stagiaires.studentnr;

Alle namen van de studenten worden vermeld, en voor de studenten die stage lopen ook het nummer van hun stage en van hun begeleider.

3.2.3 RIGHT JOIN

Een right outer join bevat alle records uit de tabel table2 aan de many zijde van de relatie en enkel die records uit de tabel table1 aan de one zijde van de relatie waarvoor een corresponderend record in de andere (many) tabel table2 bestaat. Deze join wordt aangeduid met het jointype RIGHT. De join wordt een right outer join genoemd omdat de tabel aan de many zijde van de relatie steeds rechts van de sleutelwoorden RIGHT JOIN staat.

SELECT fieldlist
FROM table1 RIGHT JOIN table2
ON table1.field1 = table2.field2;

Voorbeeld (one to many relatie Studenten - Stagiaires):

SELECT Stagiaires.studentnr, achternaam, voornaam, stagenr, begeleidersnr
FROM Studenten RIGHT JOIN Stagiares ON Studenten.studentnr = Stagiaires.studentnr;

Alle nummers van stagiaires worden vermeld, met daarbij de nummers van de studenten, stages en begeleiders.

Omdat met elke stagiare (per definitie) een student correspondeert, is het resultaat hetzelfde als bij de inner join. Zoiets gebeurt zeer vaak bij right outer joins.

Right outer joins treden in de praktijk vooral op bij many to many relaties die opgesplitst zijn in een one to many en een many to one relatie.

Voorbeeld (many to many relatie Studenten - Stagiaires - Begeleiders):

SELECT Studenten.studentnr, Studenten.achternaam, Studenten.voornaam, stagenr, Stagiares.begeleidersnr, Begeleiders.voornaam
FROM Begeleiders RIGHT JOIN (Studenten LEFT JOIN Stagiaires
ON Studenten.studentnr = Stagiaires.studentnr)
ON Begeleiders.begeleidersnr = Stagiaires.begeleidersnr;

De plaatsing van de ( ) en de volgorde van de tabellen is steeds zó dat een one tabel steeds links van de gejoinde many tabel (of combinatie) staat:

Het combineren van uitsluitend inner joins onderling is steeds toegelaten. Het combineren van uitsluitend outer joins is steeds toegelaten wanneer de tabel waarvan alle rijen getoond worden een outer join vertoont met alle andere tabellen (dit betekent in de grafische voorstelling van de joins in het Query Design View dat opeenvolgende pijlen nooit naar elkaar toe mogen wijzen). Het combineren van inner en outer joins is echter meestal niet toegelaten. Bij het bewaren of tonen van de dynaset treedt dan steeds een foutmelding op.

Wanneer bij dezelfde many to many relatie alle begeleiders en enkel de studenten met een begeleider moeten worden getoond, neemt het SQL statement de volgende gedaante aan:

SELECT Stagiaires.studentnr, Studenten.achternaam, Studenten.voornaam, Stagiaires.stagenr, Begeleiders.begeleidersnr, Begeleiders.voornaam
FROM Studenten RIGHT JOIN (Begeleiders LEFT JOIN Stagiaires
ON Begeleiders.begeleidersnr = Stagiaires.begeleidersnr)
ON Studenten.studentnr = Stagiaires.studentnr;

3.3 Voorbeelden (*)

Hierna volgen de SQL statements van de queries uit het handboek 'Basiscursus Access 97' [1]:

Oefening 4.3:

SELECT [type instelling], naam, plaats, telefoon, contactpersoon
FROM Instellingen;

Oefening 4.9:

SELECT Studenten.studentnr, voornaam, achternaam, stagenr, begeleidersnr
FROM Studenten LEFT JOIN Stagiaires ON Studenten.studentnr = Stagiaires.studentnr;

Oefening 4.10:

SELECT Stages.stagenr, [begin stage], stagiairnr, studentnr, begeleidersnr
FROM Stages INNER JOIN Stagiaires ON Stages.stagenr = Stagiaires.stagenr;

Oefening 4.11:

SELECT Begeleiders.begeleidersnr, achternaam, stagiairnr
FROM Begeleiders INNER JOIN Stagiaires ON Begeleiders.begeleidersnr = Stagiaires.begeleidersnr;

Oefening 4.12:

SELECT stagenr, Instellingen.instellingsnr, naam, plaats
FROM Instellingen INNER JOIN Stages ON Instellingen.instellingsnr = Stages.instellingsnr;

Oefening 4.13:

SELECT achternaam, voornaam, adres, postcode, woonplaats, telefoon
FROM Studenten
WHERE woonplaats = "Heerenveen";

Oefening 4.13:

SELECT achternaam, voornaam, adres, postcode, woonplaats, telefoon
FROM Studenten
ORDER BY voornaam;

Oefening 4.19:

SELECT Studenten.studentnr, achternaam, Stages.instellingsnr, Begeleiders.begeleidersnr, Stagiaires.stagiairnr
FROM Studenten INNER JOIN (Stages INNER JOIN (Begeleiders INNER JOIN Stagiaires
ON Begeleiders.begeleidersnr = Stagiaires.begeleidersnr) ON Stages.stagenr = Stagiaires.stagenr)
ON Studenten.studentnr = Stagiaires.studentnr
ORDER BY achternaam;

Oefening 5.1:

SELECT studentnr, achternaam, woonplaats, geb_datum
FROM Studenten
WHERE (woonplaats="Sneek" Or woonplaats="Heerenveen")
AND (geb_datum>=#1/1/74# And geb_datum<=#1/1/75#);

Oefening 5.2:

SELECT achternaam, woonplaats
FROM Studenten
WHERE achternaam Like "M*" AND woonplaats = "Heerenveen";

Oefening 5.3:

SELECT naam, plaats, [type instelling]
FROM Instellingen
WHERE (plaats="Sneek" Or plaats="Heerenveen")
AND ([type instelling]="School" Or [type instelling]="Zorgverlening");

Oefening 5.4:

SELECT stagenr, instellingsnr, stagedagen
FROM Stages
WHERE stagedagen Is Not Null;

Oefening 5.5:

SELECT voornaam, achternaam, geb_datum
FROM Studenten
WHERE geb_datum Like "23/02/*";

Oefening 5.6:

SELECT instellingsnr, naam, plaats, telefoon
FROM Instellingen
WHERE plaats In ("Sneek");

Oefening 5.7:

SELECT stagenr, [begin stage], [einde stage]
FROM Stages
WHERE [begin stage] > #2/15/97#
AND [einde stage] < #6/15/97#;

Oefening 5.8:

SELECT Stagiaires.stagiairnr, Begeleiders.begeleidersnr, achternaam, beg_uren, reistijd
FROM Instellingen INNER JOIN (Stages INNER JOIN (Begeleiders INNER JOIN Stagiaires
ON Begeleiders.begeleidersnr = Stagiaires.begeleidersnr) ON Stages.stagenr = Stagiaires.stagenr)
ON Instellingen.instellingsnr = Stages.instellingsnr
WHERE (Begeleiders.begeleidersnr)=3;

Oefening 5.9:

SELECT achternaam, SUM(geb_uren), SUM(reistijd)
FROM [Overzicht begeleider Galama]
GROUP BY achternaam;

Oefening 5.15:

SELECT Stagiaires.stagiairnr, Studenten.studentnr, Stages.stagenr, achternaam, woonplaats, Instellingen.instellingsnr, naam, plaats, Instellingen.telefoon, [type instelling], contactpersoon, reistijd
FROM Studenten INNER JOIN ((Instellingen INNER JOIN Stages ON Instellingen.instellingsnr = Stages.instellingsnr) INNER JOIN Stagiaires ON Stages.stagenr = Stagiaires.stagenr)
ON Studenten.studentnr = Stagiaires.studentnr
ORDER BY Stagiaires.stagiairnr;

4. De INSERT, UPDATE en DELETE statements

Het aanbrengen van wijzigingen aan één of meer tabellen valt uiteen in drie categorieën:

SQL heeft voor elk van deze bewerkingen een statement.

4.1 INSERT

Je kan één enkel record aan een bestaande tabel toevoegen met het statement

INSERT INTO tablename [(fieldname [, ...])] VALUES (value [, ...]);

Aan de tabel tablename worden de waarden value, ... toegevoegd in de velden fieldname, ... Dit komt overeen met het rechtstreeks intypen van nieuwe waarden in een tabel. Wanneer je geen veldnamen opgeeft, bepaalt de volgorde van de velden in tablename in welk veld de waarden worden opgenomen. Er moeten dan evenveel values zijn als velden in de tabel tablename.

Daarnaast heeft Access twee queries om records uit één of meerdere tabellen te selecteren en toe te voegen aan een nieuwe of bestaande tabel:

Met de Make Table Query bewaar je de dynaset van een SELECT query in een nieuwe tabel. De SQL syntaxis is:

SELECT fieldlist INTO tablename
FROM tablelist
WHERE condition
GROUP BY group_fieldlist
HAVING group_condition
ORDER BY fieldlist;

Het enige verschil met een gewone SELECT query wordt gevormd door de INTO tablename clause, waarin de naam van de nieuwe tabel wordt vermeld. De velden in deze nieuwe tabel zijn de velden uit de fieldlist. Je kan de velden in de nieuwe tabel een andere naam geven door aliassen te gebruiken in de fieldlist. De tabel tablename zal niet automatisch een primaire sleutel krijgen!

Met de Append Query voeg je de dynaset van een SELECT query toe aan een reeds bestaande tabel. De SQL syntaxis is:

INSERT INTO tablename [(fieldlist1)]
SELECT fieldlist2
FROM tablelist
WHERE condition
GROUP BY group_fieldlist
HAVING group_condition
ORDER BY fieldlist;

Het SELECT statement wordt voorafgegaan door de INSERT INTO clause, waarin je de tabel en de veldnamen in de tabel kan vermelden waarin de geselecteerde velden moeten worden geplaatst.

Indien fieldlist1 ontbreekt, wordt aangenomen dat de velden in tablename dezelfde naam hebben als die in fieldlist2. Indien dit niet het geval is zal de volgorde van de velden in tablename en fieldlist2 de overeenkomst bepalen.

Indien fieldlist1 niet ontbreekt, wordt de overeenkomst van de velden bepaald door gelijke namen in fieldlist1 en fieldlist2. Indien er geen overeenkomst is, is opnieuw de volgorde van de velden in fieldlist1 en fieldlist2 bepalend.

Steeds moet het aantal namen in fieldlist1 (of tablename wanneer fieldlist1 ontbreekt) en fieldlist2 overeenkomen.

In het algemeen doe je er goed aan de namen én de volgorde van de velden in de dynaset van het SELECT statement en de INSERT INTO clause te laten overeenkomen. Velden waarvoor geen overeenkomst bestaat worden immers niet aan de tabel tablename toegevoegd.

Nieuwe records worden achteraan de bestaande tabel toegevoegd, in de volgorde bepaald door het SELECT statement. Wanneer de waarden van de primaire sleutel van reeds bestaande en nieuw toegevoegde records gelijk zijn, zullen deze nieuwe records niet worden toegevoegd. De primaire sleutel mag immers geen dubbele waarden aannemen.

De Make Table en Append Queries dupliceren gegevens. Dit is meestal niet gewenst vanuit het database standpunt, en wordt daarom niet erg vaak gedaan. Situaties waarbij dit wel zinvol is, zijn momentopnames die bewaard moeten worden, backups en tabellen bestemd voor gebruik door andere toepassingen, die niet met dynasets kunnen omgaan. Ook het opsplitsen van een tabel in twee nieuwe tabellen met een one to one relatie ertussen kan je met INSERT INTO uitvoeren. Je moet dan achteraf nog wel de primaire en vreemde sleutels definiëren.

Voorbeelden:

INSERT INTO Studenten (studentnr, achternaam, voornaam)
VALUES (25, Vervliet, Willy);

SELECT studentnr, achternaam, voornaam INTO Namen
FROM Studenten WHERE geb_datum <#1/1/75#;

INSERT INTO Namen
SELECT studentnr, achternaam, voornaam
FROM Studenten
WHERE geb_datum>=#1/1/75#;

4.2 UPDATE

Met een Update Query kan je de waarden van één of meer velden in een tabel wijzigen. De syntaxis van het SQL statement is:

UPDATE tablename SET fieldname = expression [, ...]
WHERE condition;

In de UPDATE clause geef je met tablename de te wijzigen tabel aan. Vervolgens vermeld je in de SET clause de te wijzigen velden en hun nieuwe waarden. Deze nieuwe waarden kunnen constanten (tekst of getallen) zijn, maar ook berekende formules. De records waarbij dit moet gebeuren kan je bepalen met een WHERE clause. Indien de WHERE clause ontbreekt worden de wijzigingen in alle records van de tabel aangebracht.

Voorbeelden:

UPDATE Studenten SET jaar = 1997, klas = 2KA;

UPDATE Begeleiders SET vergoeding = vergoeding + reistijd * 10
WHERE beschikbaar = Yes;

UPDATE Begeleiders SET vak = Null;

4.3 DELETE

Met een Delete Query kan je één of meer records uit een tabel verwijderen. De syntaxis van het SQL statement is:

DELETE [*] FROM tablename
WHERE condition;

In de DELETE clause kan je (facultatief) met de * aangeven dat je volledige records wilt verwijderen. In de FROM clause geef je de naam tablename van de tabel op. Welke records moeten worden verwijderd kan je bepalen met een WHERE clause. Indien de WHERE clause ontbreekt worden alle records verwijderd, en de tabel dus leeggemaakt.

Voorbeelden:

DELETE * FROM Stages;

DELETE * FROM Studenten WHERE woonplaats = "Gent";

Referenties

[1] Boertjens, Koos, "Basiscursus Access 97 UK versie"
Academic Service 1997
ISBN 90 395 0683 3

Geschiedenis van deze pagina