Domeinmodellering/2 Activiteiten/Assignments/Opdracht02-B02/4 Extra oefening voor toets

Uit Werkplaats
Ga naar: navigatie, zoeken

Domeinmodellering
BCFA-IPI004




Een alternatieve ‘sample database’ om het opstellen van SQL-queries nog eens extra mee te oefenen …


Voor als je nog wat extra wilt oefenen met het opstellen van SQL-queries geven we hier een schets van een 'papieren [dus niet echt, fysiek bestaande] database'. Dat heeft als voordeel, dat je niet in staat bent om aan de verleiding toe te geven om niet te gaan nadenken, maar maar wat uit te proberen (hacken) achter het toetsenbord.

Je zult je voor het maken van deze opdracht puur moeten baseren op:
  - je kennis van de mogelijke constructies voor SQL-queries
  - en je analyse van het probleem.


Let op: het is NIET de bedoeling dat degenen die 'goed zijn' in het opstellen van SQL-queries, hier deze vragen gaan beantwoorden. Het is net WEL de bedoeling dat degenen die zich zelf niet zo sterk vinden in het opstellen van SQL-queries hier kunnen pogen een goed antwoord te formuleren en als dan 'de beteren' zo'n uitwerking zien en het idee hebben dat ze er een positieve feedback (kortom: op een prettige wijze) ter ondersteuning/correctie op kunnen geven, dat dan inderdaad doen.


Tabelstructuren

Beschouw de volgende structuur en 'mini-invulling' van een relationele database.
Deze database bestaat uit 4 tabellen, "Klanten", "Vertegenwoordigers", "Producten" en "Verkopen"; van elke tabel worden hieronder enkele gevulde rijen weergegeven.


KLANTEN Tabel
Klant_nr Naam Adres Plaats Postcode
001 Janssen, J Annastraat 456 Nijmegen 6525 ZP
002 Klaassen, K Groenestraat 48 Nijmegen 6531 HS
003 Pietersen, P Dennenweg 147 Arnhem 6823 MR


VERTEGENWOORDIGERS Tabel
Vert_nr Naam Geslacht Provisie Salaris
001 Arendsen, A M 10% 90000
002 Zeeuw, Z de M 20% 40000
003 Janssen, J V 20% 50000

N.B. in werkelijkheid staan hierboven in de 'vertegenwoordigers'-tabel in de kolom 'provisie' numerieke waarden als 0.1 (voor 10%) en 0.2 (voor 20%) e.d.


PRODUCTEN Tabel
Prod_nr In_voorraad Stukprijs Gewicht
001 54 99.99 12.5 kg
002 72 238.99 -
003 458 50.00 1.3 kg

N.B. In de 'producten'-tabel hoeft 'gewicht' niet ingevuld te zijn (in feite zijn waarden in deze kolom numeriek; voor de leesbaarheid is hier 'kg' toegevoegd).


VERKOPEN Tabel
Verkoop_nr Prod_nr Aantal Bedrag Vert_nr Klant_nr
001 001 1 99.99 001 002
002 001 2 199.98 002 001
003 002 1 238.99 001 003


(N.B. de getoonde database is een variant op: http: // www.extropia. com/ tutorials/sql/create.html )


De vragen bijelkaar

De vragen die je met de hulp en het commentaar van anderen hier kunt beantwoorden zijn resp. :

  1. Geef voor de beide geslachten 'vertegenwoordigers' (dus: 'per geslacht') aan, voor hoeveel ze in totaal verkochten.
  2. Aan welke klanten zijn producten verkocht waarvan in de database geen 'gewicht' is opgenomen. Toon de namen en volledige adressen van die klanten.
  3. Toon per vertegenwoordiger het totale provisie-bedrag dat hij/zij voor het totaal van zijn/haar verkopen krijgt.
  4. Geef van het product, waarvan voor de grootste totaalwaarde ('totaalbedrag') artikelen in voorraad zijn, het productnummer en die totaalwaarde. (N.B. als er meerdere producten zijn met eenzelfde 'hoogste totaalwaarde', dan moeten van al die 'meerdere producten' de gevraagde gegevens getoond worden.)
  5. Geef een overzicht van alle klanten (toon hun namen) waaraan minder dan 3 verkopen hebben plaatsgevonden.
  6. Toon van de vertegenwoordigers de gegevens van degenen die per geslacht het hoogste salaris hebben. Hint: gebruik een correlated query-constructie.
  7. Van welke bestaande producten zijn nog geen verkopen geregistreerd en is het gewicht méér dan 10 kg?
  8. Toon de gegevens van de vertegenwoordigers die in totaal meer dan 5 verschillende producten aan klanten hebben verkocht.
  9. Aan klanten uit welke stad zijn voor het hoogste totaalbedrag producten verkocht? Toon de naam van die stad en het bijbehorende [hoogste] totaalbedrag.
  10. Geef per stad aan, aan welke klant uit die stad het meest (in geld) verkocht is. Toon naast de naam van elke stad ook de naam van die 'beste' klant.


Aanpak

Bestudeer uiteraard eerst opnieuw de in de colleges behandelde stof. Kijk beslist ook naar de op het college/werkcollege besproken sheets over 'foutieve queries'.

Stel pas daarna SQL-queries op om een antwoord te vinden op de hiernavolgende informatievragen. Ga systematisch te werk en analyseer je probleem eerst op o.a.:

  • wat moet getoond worden?
  • condities?
  • vereiste tabellen?

en vraag je pas daarna af welke SQL-constructen gebruikt moeten worden, zoals:

  • Joins ? ('simple', left -, right -?)
  • Subqueries ? (correlated?)
  • WHERE .. ? ORDER BY ?
  • GROUP BY ? HAVING ?
  • >= ALL .. ? … NOT IN … ?
  • Views / assimilated queries / others  ?



De afzonderlijke vragen met mogelijke antwoorden en feedback erop:
Nogmaals: laat het beantwoorden van de vragen over aan degenen die zichzelf niet zo goed vinden in het opstellen van SQL-queries en laat degenen die daar wel goed in zijn, zich beperken tot het geven van suggesties/feedback.


Je kunt hier dus in samenwerking met anderen tot een correct antwoord (SQL-query) komen!
Klik gewoon bij de betreffende vraag linksboven op 'bewerken' en tik je bijdrage in.


Vraag 1

1. Geef voor de beide geslachten 'vertegenwoordigers' (dus: 'per geslacht') aan, voor hoeveel ze in totaal verkochten.

Antwoord:
<source lang='sql'> Select ( naam, geslacht, aantal from vertegenwoordigers,verkopen where vertegenwoordigers.vert_nr = verkopen.vert_nr group by naam,geslacht,aantal)</source>


Ik denk niet dat dit helemaal de bedoeling van de vraag was. Wat ik eruit op kan maken, is dat men op zoek is naar:
  • het totaalbedrag aan verkopen ("voor hoeveel ze in totaal verkochten")
  • per geslacht ("geef voor beide geslachten vertegenwoordigers")

Ik zou dus verwachten dat er iets uit moet komen in de richting van:

GESLACHT	TOTAAL_BEDRAG_VERKOPEN
M		538.96
V		0

Hier is in ieder geval--zoals je zelf al goed had gezien--een join tussen de VERTEGENWOORDIGERS- en de VERKOPEN-tabel voor nodig. Waar je denk ik de mist in gaat, is bij de GROUP BY: wat we willen, zijn groepen voor voor ieder geslacht (in de vraagstelling is zoiets meestal te herkennen aan "per geslacht"); met groepen verkopen per individuele vertegenwoordiger kunnen we hier niet zoveel.

Nog een kleine opmerking: de haakjes achter je SELECT mag (moet?) je trouwens weglaten; die heb je alleen in de SELECT nodig als je met assimilated queries gaat werken (een subquery in de SELECT-clausule van een andere query; zie slide 32 van week 2).

PS: Mooi dat er alsnog iemand aan de slag is gegaan met de oefenvragen!
Tom Sanders.jpg
Tom SandersDomeinmodellering Remove this comment when resolved!



Ik ben er ook naar aan het kijken, en ik vraag me af of je een Left Join moet gebruiken omdat het ook kan zijn dat een geslacht niets heeft verkocht, maar je dat wel wilt zien. Dus ik kom op het volgende uit:
Ben Brücker.jpg
Ben BrückerDomeinmodellering Remove this comment when resolved!


<source lang='sql'> Select Geslacht, Sum (Bedrag * Aantal) From Vertegenwoordiger V Left Join Verkopen K

    On V.Vert_nr = K.Vert_nr

Group By Geslacht</source>


Daar zul je inderdaad een LEFT JOIN voor nodig hebben. Ik denk overigens dat de waarde voor VERKOPEN.BEDAG al het totaalbedrag (aantal * stukprijs) is: in de voorbeeldpopulatie bij VERKOPEN staat bij de rij met Verkoop_nr 002 een bedrag van 199.98 voor 2 producten met stukprijs 99.99. Als dat inderdaad zo is, dan zou het dus simpelweg SUM(Bedrag) moeten zijn.
Tom Sanders.jpg
Tom SandersDomeinmodellering Remove this comment when resolved!


Vraag 2

2. Aan welke klanten zijn producten verkocht waarvan in de database geen 'gewicht' is opgenomen. Toon de namen en volledige adressen van die klanten.

Antwoord:
<source lang = 'sql'> select (naam,adres,plaats,postcode from klanten,verkopen,producten where klanten.klant_nr = verkopen.klant_nr AND verkopen.prod_nr =producten.prod_nr AND producten.gewicht is null group by naam, adres, plaats, postcode)</source>


Hier wederom de opmerking m.b.t. de haakjes, maar voor de rest ziet hij er goed uit! In plaats van de GROUP BY naam, adres, plaats, postcode, zou je misschien SELECT DISTINCT naam, adres, plaats, postcode kunnen gebruiken; dat vind ik altijd een wat nettere oplossing.
Tom Sanders.jpg
Tom SandersDomeinmodellering Remove this comment when resolved!


Antwoord met subqueries i.p.v. joins:
<source lang = 'sql'> SELECT NAAM, ADRES, POSTCODE, PLAATS FROM KLANTEN WHERE KLANT_NR IN (SELECT KLANT_NR

                  FROM VERKOPEN
                  WHERE PROD_NR IN (SELECT PROD_NR
                                    FROM PRODUCTEN
                                    WHERE GEWICHT IS NULL))</source>

Dit kan ook lijkt me toch? Ik vind persoonlijk subqueries veel makkelijker werken dan joins.

Vraag 3

3. Toon per vertegenwoordiger het totale provisie-bedrag dat hij/zij voor het totaal van zijn/haar verkopen krijgt.

Antwoord:
<source lang = 'sql'> Select Distinct Naam, Sum (Provisie * Bedrag * Aantal) From Vertegenwoordigers V, Verkopen K Where V.Vert_nr = k.Vert_nr </source>


Voordat ik begin over wat hier nou eigenlijk gevraagd wordt is het belangrijk om eerst de syntax van de query te controleren. Kun je je nog herinneren wanneer je een GROUP BY moest gebruiken? Ik zal een hint geven, het heeft namelijk te maken met functies zoals COUNT, SUM etc. Daarnaast wordt hier gevraagd naar het totaal van de vertegenwoordiger zijn/haar verkopen. Een vertegenwoordiger verkoopt meerdere (en verschillende) producten. Je moet dus het totale bedrag van één vertegenwoordiger gebruiken die uit het totaal van meedere producten bestaat.
Bas Elbers.jpg
Bas ElbersDomeinmodellering Remove this comment when resolved!



Bovendien geldt, dat je -bij géén gebruik van een GROUP BY- géén combinatie van kolomnamen en statistische functie in je SELECT-regel mag gebruiken [iets waar jij je niks van aantrekt], mede omdat dat betekenisloos is.
Ger Paulussen.jpg
Ger PaulussenDomeinmodellering Remove this comment when resolved!



{{{3}}}
Lars Bade.jpg
Lars BadeDomeinmodellering Remove this comment when resolved!



De opmerking hierboven over twee vertegenwoordigers met dezelfde naam, is in principe heel erg correct, maar hier ietwat onlogisch omdat in de SELECT-regel gevraagd wordt om de naam... Daarom zou ikzelf ook alleen groeperen op naam.
Ger Paulussen.jpg
Ger PaulussenDomeinmodellering Remove this comment when resolved!


Vraag 4

4. Geef van het product, waarvan voor de grootste totaalwaarde ('totaalbedrag') artikelen in voorraad zijn, het productnummer en die totaalwaarde. (N.B. als er meerdere producten zijn met eenzelfde 'hoogste totaalwaarde', dan moeten van al die 'meerdere producten' de gevraagde gegevens getoond worden.)

Antwoord:
<source lang='sql'> Select Distinct Prod_nr, (In_Voorraad * stukprijs) From Producten Where (In_voorraad * stukprijs) = MAX(In_voorraad * stukprijs)

</source>


Kijk eens naar de sheets van de werkcollege van maandag. Het heeft te maken met de MAX in je WHERE-clausule (2e sheet 2e voorbeeld). Ikzelf vroeg me af of dit met een VIEW kon vanwege de N.B.?)
Bas Elbers.jpg
Bas ElbersDomeinmodellering Remove this comment when resolved!



Ik zat te denken dat de statistische functie wel kon als je het als een vergelijking gebruikt waar een boolean uitkomt. Maar je hebt natuurlijk gelijk gezien dit hier alleen van toepassing is op een enkele rij.
Ben Brücker.jpg
Ben BrückerDomeinmodellering Remove this comment when resolved!


Dit was mijn poging:

SELECT Prod_nr, SUM(In_Voorraad * Stukprijs)
FROM PRODUCTEN
GROUP BY Prod_nr
HAVING SUM(In_Voorraad * Stukprijs) >= ALL
(SELECT  SUM(In_Voorraad * Stukprijs)
 FROM PRODUCTEN
 GROUP BY Prod_nr)

Wat valt er te zeggen over onderstaande query?

CREATE VIEW totaal_artikelen (Product_nr, totaalbedrag) AS
SELECT Prod_nr, Stukprijs*In_voorraad
FROM PRODUCTEN

SELECT Prod_nr, MAX(totaal_bedrag)
FROM totaal_artikelen
GROUP BY Prod_nr


De tweede query met de view lijkt mij wel goed. Ik weet niet of dat bijster efficiënt is, maar ik vind het wel elegant. Klein schoonheidsfoutje is alleen dat je totaalbedrag een keer met _ en een keer zonder hebt. Kun je de having van de andere query niet gewoon maken als:
Ben Brücker.jpg
Ben BrückerDomeinmodellering Remove this comment when resolved!


HAVING SUM(In_Voorraad * Stukprijs) = MAX (SUM(In_Voorraad * Stukprijs))

Ik heb het als volgt probeert en volgens mij klopt dat zo, of niet?


Schoonheidsfoutjes kosten je het tentamen niet, maar wel goed gezien ;). Voor jouw suggestie verwijs ik je graag naar de standaardfouten die Ger liet zien op het werkcollege maandag (Some SQL-queries with errors op BB, pagina 4 en 5 van de sheets). Hou gewoon aan dat je nóóit iets als MAX(COUNT) of AVG(MAX) of wat dan ook gebruikt, nergens niet. Het enige wat wél kan is dus éérst een VIEW maken en dan de MAX van die VIEW nemen (maar dan kan het ook zonder VIEW!).
Bas Elbers.jpg
Bas ElbersDomeinmodellering Remove this comment when resolved!

<source lang='sql'> SELECT Prod_nr, In_vooraad*Stukprijs AS totaalwaarde FROM Producten WHERE In_vooraad*Stukprijs >= ALL(SELECT In_voorraad*Stukprijs

                                 FROM Producten)

</source>


Ik ben hier ook geen expert in, maar volgens mij is dit inderdaad correct. Het is daarnaast een stuk korter (en als je het aan mij vraag beter) dan de mijne. Goedzo :D.
Bas Elbers.jpg
Bas ElbersDomeinmodellering Remove this comment when resolved!


Vraag 5

5. Geef een overzicht van alle klanten (toon hun namen) waaraan minder dan 3 verkopen hebben plaatsgevonden.

Antwoord:
<source lang = 'sql'> SELECT K.Naam FROM Klanten K, Verkopen VK WHERE K.Klant_nr = VK.Klant_nr GROUP BY K.Klant_nr, K.Naam HAVING COUNT(*) < 3 </source>


Hmm, wellicht dat dit equivalent is aan mijn onderstaande query, maar dat durf ik niet te zeggen.
SELECT Naam
FROM KLANTEN
WHERE Klant_nr IN
(SELECT Klant_nr)
 FROM VERKOPEN
 GROUP BY Klant_nr
 HAVING COUNT(*) < 3)

Bas Elbers.jpg
Bas ElbersDomeinmodellering Remove this comment when resolved!



Volgens mij zijn deze twee queries identiek, behalve van het feit, dat jouw oplossing een SubQuery bevat een de mijne een Join.
Lars Bade.jpg
Lars BadeDomeinmodellering Remove this comment when resolved!


Vraag 6

6. Toon van de vertegenwoordigers de gegevens van degenen die per geslacht het hoogste salaris hebben. Hint: gebruik een correlated query-constructie.

Antwoord: <source lang = 'sql'>


</source>


Vraag 7

7. Van welke bestaande producten zijn nog geen verkopen geregistreerd en is het gewicht méér dan 10 kg?

Antwoord:
<source lang='sql'>


</source>


Vraag 8

8. Toon de gegevens van de vertegenwoordigers die in totaal meer dan 5 verschillende producten aan klanten hebben verkocht.

Antwoord:
Dit is eigenlijk de enige query bij die ik erg onzeker ben of ze juist is. <source lang='sql'> SELECT * FROM Vertegenwoordigers WHERE Vert_nr IN (SELECT Vert_nr

                 FROM Verkopen
                 GROUP BY Vert_nr
                 HAVING COUNT(DISTINCT Prod_nr) > 5)

</source>


Volgens zegt deze query exact naar wat er gevraagd wordt. Je kunt hier goed nagaan wat de query precies laat zien, maar wellicht kan iemand anders het nog bevestigen?)
Bas Elbers.jpg
Bas ElbersDomeinmodellering Remove this comment when resolved!


Vraag 9

9. Aan klanten uit welke stad zijn voor het hoogste totaalbedrag producten verkocht? Toon de naam van die stad en het bijbehorende [hoogste] totaalbedrag.

Antwoord:
Dit is naar aanleiding van het commentaar van Lars bij opdracht 10. Ik snap niet hoe dit dan moet... Je moet groeperen in het 2e deel van de subquery, want je wil alles bij elkaar hebben geteld pér stad. Daarvoor heb je de tabel KLANTEN nodig, maar die kun je niet gebruiken omdat je SUM(Bedrag) met elkaar aant het vergelijken bent? Daarom gebruik ik dus een VIEW :).


<source lang='sql'>

SELECT Plaats,SUM(Bedrag) FROM VERKOPEN V, KLANTEN K WHERE V.Klant_nr = K.Klant_nr GROUP BY K.Plaats HAVING SUM(Bedrag) >= ALL

                    (SELECT SUM(Bedrag)
                     FROM VERKOPEN V2
                     WHERE V2.Klant_nr = K.Klant_nr
                     GROUP BY K.Plaats)

Deze laatste GROUP BY mag niet, hij zou K.Plaats niet herkennen(?) en dus probeer ik:

SELECT Plaats,SUM(Bedrag) FROM VERKOPEN V, KLANTEN K WHERE V.Klant_nr = K.Klant_nr GROUP BY K.Plaats HAVING SUM(Bedrag) >= ALL

                    (SELECT SUM(Bedrag)
                     FROM VERKOPEN V2
                     WHERE V2.Klant_nr = (SELECT Klant_nr
                                          FROM KLANTEN K2
                                          GROUP BY Plaats, Klant_nr))

</source>


{{{3}}}
Lars Bade.jpg
Lars BadeDomeinmodellering Remove this comment when resolved!



Aha, je mag daar dus wel gewoon een JOIN gebruiken. Bedankt nog voor je reactie! Hopelijk is dit genoeg om het tentamen door te komen :). Zo vind ik het wel duidelijk, alhoewel ik het nog steeds raar vindt dat je groepeert op een andere tabel dan dat je eigenlijk laat zien.
Bas Elbers.jpg
Bas ElbersDomeinmodellering Remove this comment when resolved!



Dat maakt eigenlijk niet veel uit omdat je twee tabellen joint. Als je twee tabellen joint heb je in principe een heel groot tabel en je kunt op alle onderdelen van deze tabel grouperen. Welke kolommen je dann uiteindeljk in de select op de scherm laat verschijnen maakt niets meer uit volgens mij.
Lars Bade.jpg
Lars BadeDomeinmodellering Remove this comment when resolved!


Vraag 10

10. Geef per stad aan, aan welke klant uit die stad het meest (in geld) verkocht is. Toon naast de naam van elke stad ook de naam van die 'beste' klant.

Antwoord:
<source lang = 'SQL'> Bij deze vind ik het te lastig om het zonder een VIEW te doen. Mijn query is als volgt:

CREATE VIEW meeste_geld (Klant_nr, Geld) AS SELECT Klant_nr,SUM(bedrag) FROM VERKOPEN GROUP BY Klant_nr

SELECT Naam, Plaats FROM KLANTEN K, meeste_geld M WHERE K.Klant_nr = M.Klant_nr AND Geld >= ALL (SELECT Geld

FROM meeste_geld M2
AND M2.plaats = K.plaats )

</source>


De aanpak is goed en ik denk ook dat deze query naar het gewenste resultaat leid, maar volgens mij kan het ook makkelijker zonder een view. Je moet alleen de main-query naar de klant_nr grouperen en dan kun je ook in de HAVING-deel de som uitruikenen en in de Subquery natuurlijk ook. Dan heb je twee keer de berekening van de som, maar geen view nodig ;)
Lars Bade.jpg
Lars BadeDomeinmodellering Remove this comment when resolved!