30.12.2012 Views

DIVISIONSEXEMPEL – RELATIONSALGEBRA OCH SQL r ÷ s ...

DIVISIONSEXEMPEL – RELATIONSALGEBRA OCH SQL r ÷ s ...

DIVISIONSEXEMPEL – RELATIONSALGEBRA OCH SQL r ÷ s ...

SHOW MORE
SHOW LESS

You also want an ePaper? Increase the reach of your titles

YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.

<strong>DIVISIONSEXEMPEL</strong> <strong>–</strong> <strong>RELATIONSALGEBRA</strong> <strong>OCH</strong> <strong>SQL</strong><br />

r <strong>÷</strong> s använder vi för att uttrycka frågor där ordet “alla” figurerar:<br />

Ex. “Vilka personer har stamkundskort vid ALLA klädesbutiker i stad X?”<br />

“Vilka personer har bankkonto vid ALLA banker som är verksamma i landet?”<br />

“Vilka studerande är anmälda till ALLA kurser av Soini”?<br />

“Vilka studerande är anmälda till ALLA kurser som går i period 1?”<br />

“Vilka pojkar är anmälda till de kurser som ALLA flickor tar?”<br />

“Vilka flickor är anmälda till ALLA de kurser som student nr. 40101 tar?”<br />

Vid alla dessa frågor utgör definitionen efter ALLA en mängd med vissa bestämda element.<br />

De dataenheter (personer, studenter etc.) som uppfyller alla dessa krav kommer med i<br />

resultatet. Logiken satserna uttrycker är implikation: för vilka personer gäller det att<br />

"OM det finns en klädesbutik i stad X SÅ har personen stamkundskort vid butiken” eller<br />

"OM Soini håller en kurs SÅ är studenten anmäld till den” eller<br />

“OM 40101 är anmäld till en viss kurs SÅ är flickan anmäld till samma kurs.”<br />

Vi ska studera det sista exemplet i detalj här. Å ena sidan har vi en lista (en relation) med<br />

flickor och de kurser de är anmälda till: denna blir relation r. Å andra sidan har vi en lista (en<br />

relation) av ALLA de kurser som student 40101 tar. Denna blir relation s. Nu frågar vi vilka av<br />

flickorna som tar ALLA dessa kurser. Anta, att 40101 tar följande tre kurser: G555, 456306<br />

och 456302. För att då komma med till svaret måste flickan i fråga vara anmäld till alla dessa<br />

tre kurser. (Hon får också därtill vara anmäld till andra kurser, detta påverkar inte resultatet.)<br />

Frågan uttrycks med relationsalgebra (med exempeldatabasen “kursdatabas” som finns på<br />

kursens hemsida) som<br />

Π matr, namn, kurskod (� kön = 'K' (student) |x| kursanmälan) <strong>÷</strong><br />

Π kurskod (� matr = 40101 (kursanmälan))<br />

och resultatet består av en relation med attributen namn och matr. Attributet kurskod som vi<br />

dividerar med “försvinner” vid divisionen.<br />

OBS! “Vilka personer har avklarat ALLA de kurser de är anmälda till?” har ytligt sett en<br />

liknande formulering, men logiken här är annorlunda: mängden av kurser som ska testas<br />

varierar från person till person, beroende på vilka kurser just den aktuella personen har<br />

anmält sig till. Det finns alltså inte en gemensam mängd kurser som skulle testas för varje<br />

person. Formuleringen på <strong>SQL</strong> blir ofta lättare än med division; för att uttrycka exempelfrågan<br />

räcker det med att man ur mängden av kursanmälda personer tar bort dem som har NULL<br />

som värdet av något vitsord (detta attribut finns i kursanmälan i kursexemplets sql­kod).


Vilka kvinnliga studenter tar alla de kurser som 40101 är anmäld till?<br />

Π matr, namn, kurskod (� kön = 'K' (student) |x| kursanmälan)<br />

matr namn up kön kurskod matr kurskod<br />

================================ ===========<br />

40112 Brita DT K 456306 40101 G555<br />

40112 Brita DT K 456302 40101 456306<br />

40113 Ann­Helen DT K 456304 40101 456302<br />

40113 Ann­Helen DT K 456306<br />

40113 Ann­Helen DT K 456302<br />

40128 Siru DT K G555<br />

40128 Siru DT K 456306<br />

40128 Siru DT K 456302<br />

40240 Sara IS K 456304<br />

40240 Sara IS K 456306<br />

40240 Sara IS K 456302<br />

Π matr, namn, kurskod (� kön = 'K' (student) |x| kursanmälan) <strong>÷</strong><br />

matr namn up kön kurskod matr kurskod<br />

================================ ===========<br />

40112 Brita DT K 456306 40101 G555<br />

40112 Brita DT K 456302 40101 456306<br />

40113 Ann­Helen DT K 456304 40101 456302<br />

40113 Ann­Helen DT K 456306<br />

40113 Ann­Helen DT K 456302<br />

40128 Siru DT K G555<br />

40128 Siru DT K 456306<br />

40128 Siru DT K 456302<br />

40240 Sara IS K 456304<br />

40240 Sara IS K 456306<br />

40240 Sara IS K 456302<br />

Vi ser att bara 40128, Siru har alla de önskade kurserna.<br />

Π kurskod (� matr = 40101 (kursanmälan))<br />

Π kurskod (� matr = 40101 (kursanmälan))


<strong>÷</strong> s definieras som ΠR­S(r) − ΠR­S((ΠR­S(r) × s) − ΠR­S,S(r))<br />

r


HUR ÖVERSÄTTA DETTA TILL My<strong>SQL</strong>?<br />

Problem: Det finns varken division (<strong>÷</strong>) eller mängddifferens (­) i My<strong>SQL</strong>. Det gäller alltså att<br />

gå omvägar. Det finns (åtminstone) tre olika sätt att ställa dylika frågor:<br />

1) Not exists (��) med not in (∉): (“Det får inte finnas en kurs som 40101 tar som inte finns<br />

bland de kurser den aktuella flickan tar”)<br />

select distinct matr, namn<br />

from student as R<br />

where kon = 'K' and<br />

not exists (select kurskod<br />

from kursanmalan<br />

where matr = 40101 and<br />

kurskod not in (select kurskod<br />

from kursanmalan as R2<br />

where R.matr = R2.matr));<br />

+­­­­­­­+­­­­­­+<br />

| matr | namn |<br />

+­­­­­­­+­­­­­­+<br />

| 40128 | Siru |<br />

+­­­­­­­+­­­­­­+<br />

1 row in set (0.00 sec)<br />

Den yttersta selektionen bestämmer vilka kolumner (matr, namn) vi vill se i svaret. Den<br />

yttersta select <strong>–</strong> from <strong>–</strong> where skapar tabellen med alla de kvinnliga studerandena och deras<br />

kursanmälningar. Dessa studeranden testas nu en i sänder: för att komma med i resultatet<br />

måste not exists­satsen för den aktuella studenten bli true (listan efter not exists måste bli<br />

tom).<br />

Vad består denna lista av? Först väljer man dit alla de kurser som student 40101 är anmäld till<br />

(tre st.: G555, 456306 och 456302). Dessa representeras av kurskod. Sedan testas dessa<br />

kurser en i sänder mot de kurser som den aktuella kvinnliga studenten är anmäld till: vi skapar<br />

en mängd över hennes kurser i den innersta selecten. Dessa representeras av kurskod.<br />

Denna mängd innehåller alltså alla de kurser som den aktuella kvinnliga studenten är anmäld<br />

till. Så fort vi hittar en kurskod bland 40101:s kurser som inte finns bland den aktuella flickans<br />

kurskoder, får vi ett värde till den mellersta select­listan, som härmed inte förblir tom. Detta<br />

medför att not exists blir false, och detta i sin tur innebär att den aktuella studenten inte fyller<br />

where­villkoret och inte kommer med i resultatet. Om däremot alla kurskoderna i den<br />

mellersta selektionen går att återfinna bland den aktuella kvinnliga studentens kurskoder,<br />

förblir denna select tom och not exists blir därmed sann. Nu kommer den aktuella studenten<br />

med i resultatet. Se nästa sida!


I exempelfrågan:<br />

40101:s kurser:<br />

Britas (40112) kurser:<br />

Ann­Helens (40113) kurser:<br />

Sirus (40128) kurser:<br />

Saras (40240) kurser:<br />

G555<br />

456306<br />

456302<br />

456306<br />

456302<br />

456304<br />

456306<br />

456302<br />

G555<br />

456306<br />

456302<br />

456304<br />

456306<br />

456302<br />

G555 �<br />

456306<br />

456302<br />

G555 �<br />

456306<br />

456302<br />

G555<br />

456306<br />

456302<br />

G555 �<br />

456306<br />

456302<br />

� visar vilken (vilka) kurs(er) som fattas av flickornas kursmängder. Dessa kommer med i<br />

den mellersta selektionen och gör att not exists blir false. Ifall alla 40101:s kurser går att<br />

återfinna i den aktuella flickans kursmängd, kommer flickan med i resultatet.


2) Två gånger not exists: (“Det får inte finnas en kurs som 40101 tar som inte tas av den<br />

aktuella flickan”)<br />

select distinct matr, namn<br />

from student as R<br />

where kon = 'K' and<br />

not exists (select kurskod<br />

from kursanmalan as S<br />

where matr = 40101 and<br />

not exists (select kurskod<br />

from kursanmalan as R2<br />

where R.matr = R2.matr and<br />

S.kurskod = R2.kurskod ));<br />

+­­­­­­­+­­­­­­+<br />

| matr | namn |<br />

+­­­­­­­+­­­­­­+<br />

| 40128 | Siru |<br />

+­­­­­­­+­­­­­­+<br />

1 row in set (0.01 sec)<br />

Denna lösning påminner mycket om den förra, men nu har testen med mängdtillhörighet (not<br />

in) ersatts av en not exists till. Den yttre not exists väljer ut de kurser som 40101 är anmäld till.<br />

Den inre not exists kontrollerar dessa kurser en i sänder; hittar vi samma kurs för den aktuella<br />

flickan? Om det finns en kurskod (bland flickans kurser) lika med den kurskod (av 40101:s<br />

kurser) som står i tur att testas, kommer denna kurskod att vara resultatet av den innersta<br />

selecten. Då är inte denna select tom, och då blir den inre not exists false. Nu kommer den<br />

testade kurskoden inte med i den mellersta selektionen.<br />

Varje kurskod för 40101 testas på detta sätt. Om de alla går att hitta bland den aktuella<br />

flickans kurskoder, kommer ingen av dem med i den mellersta selektionen, och då blir den<br />

yttre not exists true. Detta innebär att den aktuella flickan har alla de specificerade kurserna<br />

och hon kommer med i resultatet. Om det däremot finns en eller flera kurskoder som inte går<br />

att finna i den innersta selektionen, blir denna select tom och därmed den inre not exists true<br />

för dem, och dessa kurskoder väljs till resultatet av den mellersta selektionen. Då blir den<br />

yttre not exists falskt, och den aktuella flickan (som saknade dessa kurser) kommer inte med<br />

i resultatet.<br />

Se figuren på nästa sida!


Nästa sida!<br />

40101s kurser Britas (40112) kurser<br />

G555 Hittas inte <strong>–</strong> väljs i den mellersta selecten. 456306<br />

456306 456302<br />

456302<br />

Ann­Helens (40113) kurser<br />

G555 Hittas inte <strong>–</strong> väljs i den mellersta selecten 456306<br />

456306 456304<br />

456302 456302<br />

Sirus (40128) kurser<br />

G555 G555<br />

456306 456306<br />

456302 456302<br />

Saras (40240) kurser<br />

G555 Hittas inte <strong>–</strong> väljs i den mellersta selecten 456306<br />

456306 456304<br />

456302 456302


3) “Räkna och jämför”<br />

Idén bakom denna metod är att räkna hur många olika värden det finns i divisorn (alla de där<br />

värdena som man borde ha för att komma med i resultatet). När det gäller vårt exempel<br />

räknar vi först hur många kurser 40101 tar. Sedan kontrollerar vi hur många av dessa<br />

specifika kurser våra kvinnliga studenter tar. Om det blir samma antal, har studentflickan alla<br />

dessa kurser och kommer med i resultatet.<br />

mysql> select matr, namn<br />

from kursanmalan natural join student<br />

where kon = 'K' and<br />

kurskod in (select kurskod<br />

from kursanmalan<br />

where matr = 40101)<br />

group by matr<br />

having count(kurskod) = (select count(*)<br />

from (select kurskod<br />

from kursanmalan<br />

where matr = 40101) as tab);<br />

+­­­­­­­+­­­­­­+<br />

| matr | namn |<br />

+­­­­­­­+­­­­­­+<br />

| 40128 | Siru |<br />

+­­­­­­­+­­­­­­+<br />

1 row in set (0.00 sec)<br />

Här finns resultatet av kursanmalan natural join student where kon = 'K'<br />

and kurskod in (select kurskod from kursanmalan where matr = 40101):<br />

+­­­­­­­+­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­­+­­­­­­+<br />

| matr | kurskod | vitsord | namn | up | kon |<br />

+­­­­­­­+­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­­+­­­­­­+<br />

| 40112 | 456302 | NULL | Brita | DT | K |<br />

| 40112 | 456306 | NULL | Brita | DT | K |<br />

| 40113 | 456302 | NULL | Ann­Helen | DT | K |<br />

| 40113 | 456306 | NULL | Ann­Helen | DT | K |<br />

| 40128 | 456302 | NULL | Siru | DT | K |<br />

| 40128 | 456306 | NULL | Siru | DT | K |<br />

| 40128 | G555 | NULL | Siru | DT | K |<br />

| 40240 | 456302 | NULL | Sara | IS | K |<br />

| 40240 | 456306 | NULL | Sara | IS | K |<br />

+­­­­­­­+­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­­+­­­­­­+<br />

9 rows in set (0.00 sec)<br />

Nästa sida!


Dessa tupler ska nu grupperas enligt matrikelnumret:<br />

+­­­­­­­+­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­­+­­­­­­+<br />

| matr | kurskod | vitsord | namn | up | kon |<br />

+­­­­­­­+­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­­+­­­­­­+<br />

| 40112 | 456302 | NULL | Brita | DT | K |<br />

| 40112 | 456306 | NULL | Brita | DT | K |<br />

| 40113 | 456302 | NULL | Ann­Helen | DT | K |<br />

| 40113 | 456306 | NULL | Ann­Helen | DT | K |<br />

| 40128 | 456302 | NULL | Siru | DT | K |<br />

| 40128 | 456306 | NULL | Siru | DT | K |<br />

| 40128 | G555 | NULL | Siru | DT | K |<br />

| 40240 | 456302 | NULL | Sara | IS | K |<br />

| 40240 | 456306 | NULL | Sara | IS | K |<br />

+­­­­­­­+­­­­­­­­­+­­­­­­­­­+­­­­­­­­­­­+­­­­­­+­­­­­­+<br />

having count(kurskod) räknar nu antalet kurskoder/grupp, dvs. antalet kurser varje<br />

studentflicka har anmält sig till (observera, att vi har valt till denna selektion bara sådana<br />

kurser som 40101 tar <strong>–</strong> det är möjligt att flickorna tar andra kurser utöver dessa).<br />

having count(kurskod) = (select count(*)<br />

from (select kurskod<br />

from kursanmalan<br />

where matr = 40101) as tab);<br />

jämför antalet kurser/grupp (antalet kurser den aktuella flickan tar) med antalet kurser 40101<br />

tar (här 3 st.). Om det blir samma antal, vet vi att det är frågan om samma kurser, och då<br />

kommer flickan med i resultatet. Detta händer i den selektion som finns i början av frågan:<br />

mysql> select matr, namn<br />

from kursanmalan natural join student<br />

where kon = 'K' and<br />

kurskod in (select kurskod<br />

from kursanmalan<br />

where matr = 40101)<br />

group by matr<br />

having count(kurskod) = (select count(*)<br />

from (select kurskod<br />

from kursanmalan<br />

where matr = 40101) as tab);<br />

OBS! Här kan det bara förekomma en kursanmälan/kurs. I andra situationer måste man<br />

kanske använda count distinct för att eliminera duplikat.<br />

OBS! Det skulle vara en naturlig lösning att använda en temporär tabell för 40101:s kurser.<br />

Tyvärr får samma temporära tabell INTE öppnas två gånger i samma fråga i My<strong>SQL</strong> (5.1) så<br />

den lösningen ger bara felmeddelanden.

Hooray! Your file is uploaded and ready to be published.

Saved successfully!

Ooh no, something went wrong!