Hoofdstuk 8: Werken met SQL voor eindgebruikers
Overzicht Inleiding op SQL: de select-instructie Eenvoudige queries Join queries Nested queries Queries met setoperatoren
Structured Query Language (SQL) Een datataal voor het definiëren van een relationele database (DDL) voor het behandelen van gegevens in een relationele database (DML) raadplegen van gegevens wijzigen van gegevens toevoegen van gegevens verwijderen van gegevens Set-georïenteeerde taal SQL-opdrachten worden uitgevoerd op tabellen die geacht worden voorstellingen te zijn van genormaliseerde wiskundige relaties Het resultaat van een SQL instructie kan opnieuw een tabel zijn Geen procedureel programmeren. Theoretische onderbouwd Relationele algebra (select, project, unie, doorsnede, verschil, product, deling, join) Relationele calculus ( - er bestaat een element, - voor alle elementen) Interactief of embedded
Voorbeeld: Aankoopadministratie LEVERANCIER (LEVNR, LEVNAAM, LEVADRES, LEVPLAATS, LEVSTATUS) PRODUCT (PRODNR, PRODNAAM, PRODKLEUR, HOEV_IN_VOORR) AANKOOPVOORWAARDEN (LEVNR, PRODNR, AANKOOPPRIJS, LEVERTERMIJN) AANKOOPORDER (AONR, AODATUM, LEVNR) AANKOOPORDERREGEL (AONR, PRODNR, BESTELHOEV)
Voorbeeld: tabeldefinities CREATE TABLE LEVERANCIER (LEVNR CHAR(4) NOT NULL PRIMARY KEY, LEVNAAM VARCHAR(40) NOT NULL, LEVADRES VARCHAR(50), LEVPLAATS VARCHAR(20), LEVSTATUS SMALLINT); CREATE TABLE PRODUCT (PRODNR CHAR(6) NOT NULL PRIMARY KEY, PRODNAAM VARCHAR(40) NOT NULL, CONSTRAINT UC1 UNIQUE(PRODNAAM), PRODKLEUR VARCHAR(15), CONSTRAINT CC1 CHECK(PRODKLEUR IN (‘wit’, ‘groen’, ‘rood’, ‘grijs’, ‘oker’, ‘zwart’)), PRODGEWICHT DECIMAL (6,2), HOEV-IN-VOORR INTEGER);
Voorbeeld: tabeldefinities (vervolg) CREATE TABLE AANKOOPVOORWAARDEN (LEVNR CHAR(4) NOT NULL, AANKOOPPRIJS DECIMAL(8,2) COMMENT ON COLUMN AANKOOPPRIJS IS ‘AANKOOPPRIJS IN EUR’, LEVERTERMIJN TIME COMMENT ON COLUMN LEVERTERMIJN IS ‘LEVERTERMIJN IN DAGEN’, PRIMARY KEY (LEVNR, PRODNR), FOREIGN KEY (LEVNR) REFERENCES LEVERANCIER (LEVNR) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (PRODNR) REFERENCES PRODUCT (PRODNR) ON DELETE CASCADE ON UPDATE CASCADE);
Voorbeeld: tabeldefinities (vervolg) CREATE TABLE AANKOOPORDER (AONR CHAR(4) NOT NULL PRIMARY KEY, AODATUM DATE, LEVNR CHAR(4) NOT NULL, FOREIGN KEY (LEVNR) REFERENCES LEVERANCIER (LEVNR) ON DELETE CASCADE ON UPDATE CASCADE); CREATE TABLE AANKOOPORDERREGEL (AONR CHAR(4) NOT NULL, PRODNR CHAR(6) NOT NULL, BESTELHOEV INTEGER, PRIMARY KEY (AONR, PRODNR) FOREIGN KEY (AONR) REFERENCES AANKOOPORDER (AONR) ON DELETE CASCADE ON UPDATE CASCADE, FOREIGN KEY (PRODNR) REFERENCES PRODUCT (PRODNR)
SQL select-instructie SELECT-component FROM-component [WHERE-component] [GROUP BY-component] [HAVING-component] [ORDER BY-component]
Eenvoudige queries Onder eenvoudige queries verstaan wij vragen waarmee gegevens worden geraadpleegd die in één tabel aanwezig zijn. In de FROM component van de select-instructie is dus maar één tabelnaam vermeld. Met de SELECT component worden kolommen geëxtraheerd. Als expressies worden in de SELECT-component meestal namen gebruikt van kolommen die wij willen zien.
Eenvoudige queries zonder WHERE component SELECT LEVNR, LEVNAAM, LEVADRES, LEVPLAATS, LEVSTATUS FROM LEVERANCIER of SELECT * FROM LEVERANCIER SELECT LEVNR, LEVNAAM SELECT DISTINCT LEVNR FROM AANKOOPORDER SELECT LEVNR, PRODNR, LEVERTERMIJN/30 AS MAAND-LEVERTERMIJN FROM AANKOOPVOORWAARDEN
Eenvoudige queries met WHERE component Wanneer in een select instructie ook een WHERE component aanwezig is, kan men met condities aangeven welke tabelrijen moeten worden geselecteerd. In de WHERE component worden dus condities of predicaten opgelegd waaraan de op te halen rijen moeten voldoen. (equivalent met “select” operator van relationele algebra) Voor het specificeren van condities waaraan rijen moeten voldoen, kunnen er in de WHERE component meerdere operatoren worden gebruikt: vergelijkingsoperatoren (<, >, =, ...) booleaanse operatoren (AND, OR, NOT) BETWEEN operator (x BETWEEN y AND z) -> x ≥ y and x ≤ z IN operator (IN (u,v,w)) -> x=u or x=v or x=w LIKE operator NULL operator
Eenvoudige queries met WHERE component (voorbeelden) SELECT LEVNR, LEVNAAM FROM LEVERANCIER WHERE LEVPLAATS = ‘ANTWERPEN’; SELECT LEVNR, LEVNAAM FROM LEVERANCIER WHERE LEVPLAATS = ‘ANTWERPEN’ AND LEVSTATUS > 75; SELECT LEVNR, LEVNAAM FROM LEVERANCIER WHERE LEVSTATUS BETWEEN 75 AND 85; SELECT PRODNR, PRODNAAM FROM PRODUCT WHERE PRODKLEUR IN (‘WIT’,’GROEN’); SELECT PRODNR, PRODNAAM FROM PRODUCT WHERE PRODNAAM LIKE ‘AP%’; SELECT LEVNR, LEVNAAM FROM LEVERANCIER WHERE LEVSTATUS IS NULL
Eenvoudige queries met setfunctie in de SELECT component In de SELECT component kunnen er allerlei expressies voorkomen. Deze expressies mogen ook setfuncties zijn. Met setfuncties kunnen bewerkingen worden uitgevoerd op kolomwaarden van rijen die zich voor een bepaalde vraag kwalificeren. De belangrijkste setfuncties: COUNT MIN, MAX SUM, AVG STDEV, VARIANCE
Eenvoudige queries met setfunctie (voorbeelden) SELECT COUNT(*) FROM AANKOOPVOORWAARDEN WHERE PRODNR = ‘0117’; SELECT COUNT(AANKOOPPRIJS) SELECT COUNT (DISTINCT AANKOOPPRIJS) SELECT SUM(BESTELHOEV) AS TOTSOMBESTELLINGEN FROM AANKOOPORDERREGEL; SELECT PRODNR, SUM(BESTELHOEV) AS SOMBESTELLINGEN FROM AANKOOPORDERREGEL
Aankoopvoorwaarden levnr prodnr aankoopprijs levertermijn 05 0117 30,00 4 1245 50,00 09 30,50 5 11 31,70 6 12 - 14
Eenvoudige queries met setfunctie (voorbeelden) SELECT PRODNR, AVG(AANKOOPPRIJS) AS GEWOGEN-GEMIDDELDE-PRIJS FROM AANKOOPVOORWAARDEN WHERE PRODNR = ‘0117’; SELECT PRODNR, AVG(DISTINCT AANKOOPPRIJS) AS ONGEWOGEN-GEMIDDELDE-PRIJS SELECT PRODNR, VARIANCE(AANKOOPPRIJS) AS PRIJSVARIANTIE SELECT PRODNR, MIN(AANKOOPPRIJS) AS LAAGSTE PRIJS, MAX(AANKOOPPRIJS) AS HOOGSTE-PRIJS
Eenvoudige queries met GROUP BY en HAVING Met een GROUP BY component kunnen rijen worden gegroepeerd op basis van onderlinge overeenkomsten. bv. rijen moeten eenzelfde waarde hebben voor een gespecificeerde kolom Met de HAVING component worden groepen van rijen geselecteerd die aan bepaalde condities voldoen. de HAVING component kan enkel voorkomen als ook de GROUP BY component is gebruikt. terwijl expressies in de WHERE component geen setfunctie kunnen hebben, kan dat in de HAVING component wel.
Eenvoudige queries met GROUP BY en HAVING (voorbeelden) Gevraagd: geef de nummers van de producten waarvoor tenminste twee bestellingen uitstaan. SELECT PRODNR FROM AANKOOPORDERREGEL GROUP BY PRODNR HAVING COUNT(*) >1; Gevraagd: geef de nummers van de producten waarvoor meer dan 100 stuks in bestelling zijn. HAVING SUM(BESTELHOEV) > 100;
Eenvoudige queries met ORDER BY component Indien men de rijen – die ontstaan als resultaat van het uitvoeren van een query – in een bepaalde logische volgorde wil tonen, moet een ORDER BY component aanwezig zijn. Er mag op elke kolom, die in de SELECT component is gespecificeerd, worden gesorteerd en er mag op meer dan één kolom worden gesorteerd. default is ‘ascending’. Interpretatie van null-waarden.
Eenvoudige queries met ORDER BY (voorbeelden) Gevraagd: sorteer alle uitstaande aankooporders volgens oplopende datum, en per dag volgens dalend leveranciernummer. SELECT AONR, AODATUM, LEVNR FROM AANKOOPORDER ORDER BY AODATUM ASC, LEVNR DESC; Gevraagd: geef voor een bepaald product, de prijzen die leveranciers aanrekenen, in dalende volgorde. SELECT PRODNR, LEVNR, AANKOOPPRIJS FROM AANKOOPVOORWAARDEN WHERE PRODNR = ‘0117’ ORDER BY 3 DESC;
Join queries Met join queries kunnen gegevens worden samengevoegd of gefuseerd. Meestal gaat het om gegevens uit verschillende tabellen. in de FROM component van de select instructie worden de namen gespecificeerd van de tabellen waarvan wij rijen willen samenvoegen Wij moeten dan voorwaarden opleggen waaronder rijen van tabellen mogen worden samengevoegd. in de WHERE component worden dan de condities gespecificeerd waaromheen de join moet worden voltrokken equi-join Voorts kunnen wij aangeven in welke kolommen uit de respectievelijke tabellen wij zijn geïnteresseerd.
Join queries (vervolg) Kolommen (vervolg) wanneer een kolomnaam wordt gebruikt, die in meer dan één van de tabellen van de FROM component voorkomt, is het verplicht een tabelspecificatie voor de kolomnaam te vermelden. het is evenwel de gewoonte om kolomnamen te kwalificeren met de naam van de tabel waarin de kolom voorkomt, wanneer gegevens gevraagd worden uit meerdere tabellen. Extra condities in de WHERE component kunnen zorgen voor het fijner filteren van de rijen die voor fusie in aanmerking komen. een SQL join query zonder WHERE component waarbij alle kolommen in het resultaat mogen verschijnen is in feite een algebraïsch product (SELECT * FROM a, b)
Select instructie met inner-join (voorbeeld 1) Gevraagd: geef nummer, naam en status van leveranciers die producten kunnen leveren, tezamen met de nummers van de producten die zij kunnen leveren en de aankoopprijs die ze hiervoor aanrekenen. Leverancier (levnr, levnaam, ..., levstatus) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, ...) SELECT L.LEVNR, L.LEVNAAM, L.LEVSTATUS, V.PRODNR, V. AANKOOPPRIJS FROM LEVERANCIER L, AANKOOPVOORWAARDEN V WHERE L.LEVNR = V.LEVNR; of SELECT L.LEVNR, L.LEVNAAM, L.LEVSTATUS, V.PRODNR, V.AANKOOPPRIJS FROM LEVERANCIER AS L, INNER JOIN AANKOOPVOORWAARDEN AS V ON (L.LEVNR = V. LEVNR);
Select instructie met inner-join (voorbeeld 2) Oplossing: SELECT L.LEVNR, L.LEVNAAM, AO.AONR, AO.AODATUM, P.PRODNR, P.PRODNAAM, AOR.BESTELHOEV FROM LEVERANCIER L, AANKOOPORDER AO, AANKOOPORDERREGEL AOR, PRODUCT P WHERE (L.LEVNR = AO. LEVNR) AND (AO.AONR = AOR.AONR) AND (AOR.PRODNR = P.PRODNR);
Select instructie met inner-join (voorbeeld 3) Het is mogelijk om gegevens te “joinen” die afkomstig zijn uit rijen van eenzelfde tabel. voor het oplossen van zo’n opdracht moeten twee ‘verschijningsvormen’ van dezelfde tabel onderscheiden worden opdat het systeem de conditie kan onderzoeken waaronder respectieve rijen mogen worden samengevoegd. Gevraagd: geef alle paren van leveranciers die dezelfde vestigingsplaats hebben. Leverancier (levnr, levnaam, levadres, levplaats, levstatus) SELECT L1.LEVNAAM, L2.LEVNAAM, L1.LEVPLAATS FROM LEVERANCIER L1, LEVERANCIER L2 WHERE L1.LEVPLAATS = L2.LEVPLAATS AND (L1.LEVNR < L2.LEVNR);
Leverancier L1 Leverancier L2 levnr levnaam levadres levplaats levstatus 05 Apers Antwerpen 90 02 Denul Brussel 10 11 Lauwers Luxemburg 92 17 Haveneers 70 Leverancier L2 levnr levnaam levadres levplaats levstatus 05 Apers Antwerpen 90 02 Denul Brussel 10 11 Lauwers Luxemburg 92 17 Haveneers 70 L1.LEVNAAM L2.LEVNAAM L1.LEVPLAATS Apers Haveneers Antwerpen
Select instructie met inner-join (voorbeeld 4) Samen met de join condities mogen – uiteraard – allerlei andere condities worden opgelegd voor het uiteindelijk selecteren van de rijen. Gevraagd: de namen van de leveranciers die een specifiek product met nummer 1174 kunnen leveren. Leverancier (levnr, levnaam, levadres, levplaats, levstatus) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, levertermijn) SELECT L.LEVNAAM FROM LEVERANCIER L, AANKOOPVOORWAARDEN V WHERE L.LEVNR = V.LEVNR AND V.PRODNR = ‘1174’;
Select instructie met inner-join (voorbeeld 5) Eventuele overbodige dubbels kunnen steeds met een DISTINCT-optie worden geëlimineerd. Gevraagd: de namen van de leveranciers die tenminste één product kunnen leveren met een groene kleur. het is mogelijk dat sommige leveranciers meerdere producten met een groene kleur kunnen leveren, maar in overbodige dubbels zijn we niet geïnteresseerd Leverancier (levnr, levnaam, ...) Aankoopvoorwaarden (levnr, prodnr, ...) Product (prodnr, ..., prodkleur) SELECT DISTINCT L.LEVNAAM FROM LEVERANCIER L, AANKOOPVOORWAARDEN V, PRODUCT P WHERE L.LEVNR = V.LEVNR AND (V.PRODNR = P.PRODNR) AND (P.PRODKLEUR = ‘groen’);
Select instructie met inner-join (voorbeeld 6) Gevraagd: geef van alle producten waarvoor bestellingen zijn geplaatst, nummer, naam en totale bestelde hoeveelheid. Product (prodnr, prodnaam, …) Aankooporderregel (aonr, prodnr, bestelhoev) SELECT P.PRODNR, P.PRODNAAM, SUM(AOR.BESTELHOEV) FROM PRODUCT P, AANKOOPORDERREGEL AOR WHERE P.PRODNR = AOR.PRODNR GROUP BY AOR.PRODNR;
Select instructie met outer-join Met de inner-join zullen rijen die het systeem niet kan samenvoegen, omdat er voor de join-kolommen geen overeenstemmende waarden worden gevonden, niet aanwezig zijn in het eindresultaat. Met de outer-join constructie kan dit probleem worden opgelost. left-outer-join: elke rij uit de linkertabel wordt behouden in het eindresultaat en - zonodig – aangevuld met null-waarden voor de kolommen die uit de andere tabel komen. right-outer-join: elke rij uit de rechtertabel wordt behouden full-outer-join: elke rij uit beide tabellen wordt behouden
Select instructie met left-outer-join (voorbeeld 1) Gevraagd: geef nummer, naam en status van alle leveranciers, eventueel tezamen met de nummers van de producten die zij leveren en de aankoopprijs die ze hiervoor aanrekenen. Leverancier (levnr, levnaam, ..., levstatus) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, ...) SELECT L.LEVNR, L.LEVNAAM, L.LEVSTATUS, V.PRODNR, V. AANKOOPPRIJS FROM LEVERANCIER AS L, LEFT OUTER JOIN AANKOOPVOORWAARDEN AS V ON (L.LEVNR = V. LEVNR);
Select instructie met left-outer-join (voorbeeld 2) Gevraagd: geef van alle producten nummer, naam en totale bestelde hoeveelheid, ook indien er voor een product momenteel geen bestellingen uitstaan. Product (prodnr, prodnaam, …) Aankooporderregel (aonr, prodnr, bestelhoev) SELECT P.PRODNR, P.PRODNAAM, SUM(AOR.BESTELHOEV) AS SOM FROM PRODUCT AS P, LEFT OUTER JOIN AANKOOPORDERREGEL AS AOR ON (P.PRODNR = AOR.PRODNR) GROUP BY AOR.PRODNR;
Nested queries SELECT ... FROM ... WHERE ... (SELECT ... WHERE ... ); Als er in een select-instructie geneste select-blokken voorkomen ontstaat er een zogeheten nested query met subquery’s. Het begin van een select-blok wordt met een SELECT component aangegeven. SELECT ... FROM ... WHERE ... (SELECT ... WHERE ... ); Outer block Inner block
Scalaire subqueries (voorbeeld 1) In de where-component van een select-instructie mag als conditie worden gespecificeerd dat de waarde van een gewone expressie (bv. een kolomnaam) vergeleken moet worden met de waarde die door uitvoering van een subquery wordt afgeleverd. Gevraagd: geef de naam van leveranciers bij wie een aankooporder met een bepaald nummer is geplaatst Leverancier (levnr, levnaam, levadres, levplaats, levstatus) Aankooporder (aonr, aodatum, levnr) SELECT LEVNAAM FROM LEVERANCIER WHERE LEVNR = (SELECT LEVNR FROM AANKOOPORDER WHERE AONR = ‘1079’);
Scalaire subqueries (voorbeeld 2) Gevraagd: geef nummer en naam van elk product waarvan de voorraadhoeveelheid hoger is dan die van een bepaald product Product (prodnr, prodnaam, …, hoev_in_voorr) SELECT PRODNR, PRODNAAM FROM PRODUCT WHERE HOEV-IN-VOORR > (SELECT HOEV-IN-VOORR WHERE PRODNR = ‘0117’);
Tabel subqueries (voorbeeld 1) Select-instructies met IN operator en tabelexpressie. in de WHERE component van een outer select-blok van een select-instructie mag een IN operator worden gebruikt, gevolgd door een nieuw (inner) select-blok die een tabel-subquery mag zijn. Gevraagd: geef de namen van leveranciers die een specifiek product kunnen leveren Leverancier (levnr, levnaam, ..., levstatus) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, ...) SELECT LEVNAAM FROM LEVERANCIER WHERE LEVNR IN (SELECT LEVNR FROM AANKOOPVOORWAARDEN WHERE PRODNR = ‘0117’);
Tabel subqueries (voorbeeld 2) Gevraagd: geef de namen van leveranciers die tenminste één product kunnen leveren met een groene kleur Leverancier (levnr, levnaam, ...) Aankoopvoorwaarden (levnr, prodnr, ...) Product (prodnr, ..., prodkleur) SELECT LEVNAAM FROM LEVERANCIER WHERE LEVNR IN (SELECT LEVNR FROM AANKOOPVOORWAARDEN WHERE PRODNR IN (SELECT PRODNR FROM PRODUCT WHERE PRODKLEUR = ‘GROEN’));
Tabel subqueries (voorbeeld 3) Gevraagd: geef de namen van de producten die zowel door de leverancier met nummer ’01’ als door de leverancier met nummer ’23’ kunnen worden geleverd Aankoopvoorwaarden (levnr, prodnr, ...) Product (prodnr, prodnaam, ..., prodkleur) SELECT PRODNAAM FROM PRODUCT WHERE PRODNR IN (SELECT PRODNR FROM AANKOOPVOORWAARDEN WHERE LEVNR = ’05’) AND PRODNR IN (SELECT PRODNR FROM AANKOOPVOORWAARDEN WHERE LEVNR = ’23’);
Gecorreleerde subqueries In alle voorgaande gevallen kon de subquery (de inner-select) volledig geëvalueerd worden vooraleer met de verwerking van een outer select-blok werd gestart. met een gecorreleerde subquery is dat niet zo. Men spreekt van een gecorreleerde subquery wanneer een inner select-blok een kolom bevat die behoort tot een tabel die in een ander outer select-blok is gespecificeerd de subquery moet telkens opnieuw worden geëvalueerd voor elke rij van de tabel die in het outer select-blok aanwezig is.
Gecorreleerde subqueries (voorbeeld 1) Gevraagd: geef de nummers van de producten waarvoor er tenminste twee bestellingen uitstaan. Product (prodnr, prodnaam, …) Aankooporderregel (aonr, prodnr, bestelhoev) SELECT P.PRODNR FROM PRODUCT P WHERE 1 < (SELECT COUNT (*) FROM AANKOOPORDERREGEL AOR WHERE P.PRODNR = AOR.PRODNR);
Gecorreleerde subqueries (voorbeeld 2) Gevraagd: geef van de leveranciers die producten kunnen leveren tegen een prijs die lager ligt dan de gemiddelde prijs van dat product, nummer en naam, tezamen met nummer en naam van de betreffende producten, de prijs die door de leverancier wordt aangerekend en de levertermijn. Leverancier (levnr, levnaam, ...) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, levertermijn) Product (prodnr, prodnaam, ... ) SELECT L.LEVNR, L.LEVNAAM, P.PRODNR, P.PRODNAAM, V1.AANKOOPPRIJS, V1.LEVERTERMIJN FROM LEVERANCIER L, AANKOOPVOORWAARDEN V1, PRODUCT P WHERE L.LEVNR = V1.LEVNR AND V1.PRODNR = P.PRODNR AND V1.AANKOOPPRIJS < (SELECT AVG(AANKOOPPRIJS) FROM AANKOOPVOORWAARDEN V2 WHERE P.PRODNR = V2.PRODNR) ORDER BY L.LEVNR;
Gecorreleerde subqueries (voorbeeld 3) Gevraagd: geef de drie hoogste productnummers uit de producttabel. Redenering: het hoogste productnummer: er is geen enkel product met hoger nummer het tweede hoogste productnummer: er is één product met een hoger nummer het derde hoogste productnummer: er zijn twee producten met een hoger nummer. Werkwijze: Voor ieder product tellen wij het aantal producten met een hoger nummer; als dit aantal kleiner is dan 3, dan behoort het nummer tot de drie hoogste nummers. Oplossing: SELECT P1.PRODNR FROM PRODUCT P1 WHERE 3 > (SELECT COUNT (*) FROM PRODUCT P2 WHERE P1.PRODNR < P2.PRODNR);
Nested queries met ALL of ANY In de WHERE component van het (outer) select-blok van een select-instructie mag een ALL operator of een ANY operator worden gebruikt, die gevolgd wordt door een (inner) select-blok. de ALL operator genereert een ‘waar-antwoord’ als aan de conditie is voldaan voor alle waarden die, na uitvoering van de subquery, zijn verkregen. als de subquery geen waarde oplevert, evalueert de gehele conditie als ‘waar’. de ANY operator genereert een ‘waar-antwoord’ als aan de conditie is voldaan voor tenminste één van de waarden die, na uitvoering van de subquery, zijn verkregen. als de subquery geen waarde oplevert, evalueert de gehele conditie als ‘niet-waar’ Een conditie van de vorm =ANY(…) is equivalent aan het gebruik van de IN operator.
Nested queries met ALL (voorbeeld 1) Gevraagd: geef de namen van de leveranciers die de hoogste prijs aanrekenen voor een bepaald product. Leverancier (levnr, levnaam, ...) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, levertermijn) Oplossing: SELECT LEVNAAM FROM LEVERANCIER WHERE LEVNR IN (SELECT LEVNR FROM AANKOOPVOORWAARDEN WHERE PRODNR = ‘1245’ AND AANKOOPPRIJS ≥ ALL (SELECT AANKOOPPRIJS FROM AANKOOPVOORWAARDEN WHERE PRODNR = ‘1245’));
Nested queries met ALL (voorbeeld 2) Gevraagd: geef nummer en naam van elke leverancier waarvan de evaluatiecode het hoogst is van alle leveranciers die in zijn gemeente zijn gevestigd. Redenering: Als een leverancier in dezelfde plaats is gevestigd als één of meer andere leveranciers en zijn evaluatiecode (= levstatus) is beter of minstens gelijk aan die van alle (andere) leveranciers uit dezelfde plaats dan voldoet deze leverancier aan de voorwaarde. Oplossing: SELECT L1.LEVNR, L1.LEVNAAM, L1.LEVSTATUS FROM LEVERANCIER L1 WHERE L1.LEVSTATUS ≥ ALL (SELECT L2.LEVSTATUS FROM LEVERANCIER L2 WHERE L1.LEVPLAATS = L2.LEVPLAATS);
Nested queries met ANY (voorbeeld) Gevraagd: geef de namen van de leveranciers die niet de laagste prijs aanrekenen voor een bepaald product. Redenering: vanaf het moment dat de aankoopprijs van een product bij een bepaalde leverancier hoger is dan de prijs van dit product bij tenminste één andere leverancier is aan de voorwaarde van de vraag voldaan. Oplossing: SELECT LEVNAAM FROM LEVERANCIER WHERE LEVNR IN (SELECT LEVNR FROM AANKOOPVOORWAARDEN WHERE AANKOOPPRIJS > ANY (SELECT AANKOOPPRIJS FROM AANKOOPVOORWAARDEN WHERE PRODNR = ‘0117’));
Gecorreleerde subqueries met EXISTS De EXISTS operator wordt gebruikt om te controleren of het resultaat van de uitvoering van een gecorreleerde subquery wel of niet leeg is. vanaf het ogenblik dat de subquery minstens één rij oplevert, evalueert de conditie naar ‘waar’. als de subquery geen rijen oplevert, evalueert de conditie naar ‘niet-waar’. Omdat tijdens de evaluatie van de conditie met EXISTS operator enkel gekeken wordt of het resultaat van de uitvoering van een subquery wel of niet rijen oplevert, is het onbelangrijk wat in de select-component van de subquery staat gespecificeerd meestal gebruikt men de vorm: SELECT *.
Gecorreleerde subqueries met EXISTS (voorbeeld 1) Gevraagd: geef de namen van de leveranciers die een specifiek product kunnen leveren (reeds voorheen anders opgelost) Leverancier (levnr, levnaam, ...) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, …) SELECT LEVNAAM FROM LEVERANCIER L WHERE EXISTS (SELECT * FROM AANKOOPVOORWAARDEN V WHERE L.LEVNR = V.LEVNR AND V.PRODNR = ‘0117’);
Gecorreleerde subqueries met EXISTS (voorbeeld 2) Gevraagd: geef naam, adres en gemeente van de leveranciers die alle producten kunnen leveren Redenering: geef naam, adres en gemeente van de leveranciers waarvoor er geen enkel product bestaat dat zij niet kunnen leveren Oplossing: SELECT LEVNAAM, LEVADRES, LEVPLAATS FROM LEVERANCIER L WHERE NOT EXISTS (SELECT * FROM PRODUCT P WHERE NOT EXISTS (SELECT * FROM AANKOOPVOORWAARDEN V WHERE L.LEVNR = V.LEVNR AND P.PRODNR = V.PRODNR));
Subqueries: overzicht van de operatoren scalaire subquery-expressie: vergelijkingsoperatoren (<, <, >, >, =) tabel subquery-expressie IN operator EXISTS operator ALL operator in combinatie met vergelijkingsoperator (<, >, =, ...) ANY operator
Scalaire subquery in de SELECT component In de SELECT component mag een scalaire subquery voorkomen Gevraagd: geef van alle producten nummer, naam en totale bestelde hoeveelheid, ook indien er voor een product momenteel geen bestellingen uitstaan. Product (prodnr, prodnaam, …) Aankooporderregel (aonr, prodnr, bestelhoev) Oplossing: SELECT P.PRODNR, P.PRODNAAM, (SELECT SUM(BESTELHOEV) FROM AANKOOPORDERREGEL AOR WHERE P.PRODNR = AOR.PRODNR) AS SOM FROM PRODUCT P Vergelijk met left-outer-join (voorbeeld 2)
Tabel subquery in de FROM component Het resultaat van een tabel subquery is een tabel, die zelf weer in een FROM component mag gebruikt worden Gevraagd: geef de nummers van de producten waarvan het verschil tussen maximum en minimumprijs meer dan 100 euro bedraagt. Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, …) Oplossing: SELECT PRODNR, MINPRIJS, MAXPRIJS FROM (SELECT PRODNR, MIN(AANKOOPPRIJS), MAX(AANKOOPPRIJS) FROM AANKOOPVOORWAARDEN GROUP BY PRODNR) AS PRIJSMARGE(PRODNR, MINPRIJS, MAXPRIJS) WHERE MAXPRIJS – MINPRIJS > 100
Queries met gebruik van setoperatoren In SQL is het mogelijk om select-blokken met behulp van setoperatoren te combineren. UNION: elke rij die in het resultaat van één van de select-blokken voorkomt (of in het resultaat van beide), komt in het eindresultaat INTERSECT: enkel rijen die in het resultaat van beide select-blokken voorkomen, komen in het eindresultaat EXCEPT: in het eindresultaat komen enkel de rijen die in het eerste blok aanwezig zijn maar niet in het tweede blok In de relationele algebra komen deze operatoren overeen met: unie, doorsnede en verschil Dubbele rijen worden automatisch uit het eindresultaat verwijderd, tenzij de optie ALL zou zijn gebruikt. UNION ALL INTERSECT ALL EXCEPT ALL
Queries met setoperatoren: UNION (voorbeeld) Gevraagd: geef nummer en naam van de leveranciers die in een bepaalde stad gevestigd zijn, of een specifiek product kunnen leveren (of aan beide voorwaarden tegelijk voldoen) Leverancier (levnr, levnaam, ..., levplaats) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, …) Oplossing: SELECT LEVNR, LEVNAAM FROM LEVERANCIER WHERE LEVPLAATS = ‘BRUSSEL’ UNION SELECT LEVNR, LEVNAAM FROM LEVERANCIER L, AANKOOPVOORWAARDEN V WHERE L.LEVNR = V.LEVNR AND V.PRODNR = ‘0117’ ORDER BY LEVNAAM ASC; Bemerk: een eventuele sortering wordt op het eindresultaat toegepast (ORDER BY volgt achter het laatste select blok)
Queries met setoperatoren: INTERSECT (voorbeeld) Gevraagd: geef nummer en naam van de leveranciers die in een bepaalde stad gevestigd zijn én een specifiek product kunnen leveren Leverancier (levnr, levnaam, ..., levplaats) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, …) Oplossing: SELECT LEVNR, LEVNAAM FROM LEVERANCIER WHERE LEVPLAATS = ‘BRUSSEL’ INTERSECT SELECT LEVNR, LEVNAAM FROM LEVERANCIER L, AANKOOPVOORWAARDEN V WHERE L.LEVNR = V.LEVNR AND V.PRODNR = ‘0117’ ORDER BY LEVNAAM ASC;
Queries met setoperatoren: EXCEPT (voorbeeld) Gevraagd: geef het nummer van de leveranciers die momenteel geen producten kunnen leveren Leverancier (levnr, levnaam, ..., levplaats) Aankoopvoorwaarden (levnr, prodnr, aankoopprijs, levertermijn) Oplossing: SELECT LEVNR FROM LEVERANCIER EXCEPT SELECT LEVNR FROM AANKOOPVOORWAARDEN;
Voorbeeld: Voetbaldatabase AFDELINGEN ( RANG SMALLINT NOT NULL, AANTAL_STIJGERS SMALLINT, AANTAL_DALERS SMALLINT, PRIMARY KEY( RANG ) ); PLOEGEN ( STAMNUMMER INTEGER NOT NULL, AANSLUITINGSJAAR INTEGER, NAAM CHAR(30), STRAAT CHAR(50), HUISNUMMER SMALLINT, POSTCODE INTEGER, GEMEENTE CHAR(50), TELEFOON INTEGER, CLUB_EMBLEEM BLOB, AFDELING SMALLINT, refereert aan AFDELINGEN GEWONNEN SMALLINT, (aantal gewonnen wedstrijden) VERLOREN SMALLINT, (aantal verloren wedstrijden) GELIJK SMALLINT, (aantal gelijk gespeelde wedst) SCORE_VOOR SMALLINT, (totaal # doelpunten voor) SCORE_TEGEN SMALLINT, (totaal # doelpunten tegen) PRIMARY KEY(STAMNUMMER)
STADIA ( NAAM CHAR(30) NOT NULL, AANTAL_PLAATSEN INTEGER, PLOEG INTEGER NOT NULL, refereert aan PLOEGEN PRIMARY KEY(NAAM) ); SPELERS ( BONDSNUMMER INTEGER NOT NULL, FAMILIENAAM CHAR(30) NOT NULL, VOORNAAM CHAR(30) NOT NULL, NATIONALITEIT CHAR(30), STRAAT CHAR(50), HUISNUMMER SMALLINT, POSTCODE INTEGER, GEMEENTE CHAR(50), LAND CHAR(50), TELEFOON INTEGER, PRIMARY KEY(BONDSNUMMER) SPONSORS BUDGET INTEGER,
WEDSTRIJDEN ( NUMMER INTEGER NOT NULL, AFDELING SMALLINT NOT NULL, refereert aan AFDELINGEN SPEELDAG SMALLINT NOT NULL, DATUM DATE NOT NULL, TIJD TIME, THUISPLOEG INTEGER NOT NULL, refereert aan PLOEGEN BEZOEKERS INTEGER NOT NULL, refereert aan PLOEGEN SCORE_THUISPLOEG SMALLINT, SCORE_BEZOEKERS SMALLINT, SCHEIDSRECHTER INTEGER NOT NULL, refereert aan RECHTERS LIJNRECHTER1 INTEGER NOT NULL, refereert aan RECHTERS LIJNRECHTER2 INTEGER NOT NULL, refereert aan RECHTERS PRIMARY KEY(NUMMER) ); SELECTIES ( SPELER INTEGER NOT NULL, refereert aan SPELERS WEDSTRIJD INTEGER NOT NULL, refereert aan WEDSTRIJDEN SCORE SMALLINT NOT NULL, STRAFPUNTEN SMALLINT NOT NULL, PRIMARY KEY(SPELER,WEDSTRIJD)
TRAINERS ( BONDSNUMMER INTEGER NOT NULL, FAMILIENAAM CHAR(30) NOT NULL, NAAM CHAR(30) NOT NULL, (voornaam van de trainer) NATIONALITEIT CHAR(30), STRAAT CHAR(50), HUISNUMMER SMALLINT, POSTCODE INTEGER, GEMEENTE CHAR(50), LAND CHAR(50), TELEFOON INTEGER, PLOEG INTEGER NOT NULL, refereert aan PLOEGEN PRIMARY KEY(BONDSNUMMER) ); RECHTERS ( NUMMER INTEGER NOT NULL, FAMILIENAAM CHAR(50) NOT NULL, VOORNAAM CHAR(30) NOT NULL, PRIMARY KEY(NUMMER)
Voetbaldatabase: overzicht