DIVISIONSEXEMPEL RELATIONSALGEBRA OCH SQL r s använder vi för att uttrycka frågor där ordet alla figurerar: Ex. Vilka personer har stamkundskort vid ALLA klädesbutiker i stad X? Vilka personer har bankkonto vid ALLA banker som är verksamma i landet? Vilka studerande är anmälda till ALLA kurser av Soini? Vilka studerande är anmälda till ALLA kurser som går i period 1? Vilka pojkar är anmälda till de kurser som ALLA flickor tar? Vilka flickor är anmälda till ALLA de kurser som student nr. 40101 tar? Vid alla dessa frågor utgör definitionen efter ALLA en mängd med vissa bestämda element. De dataenheter (personer, studenter etc.) som uppfyller alla dessa krav kommer med i resultatet. Logiken satserna uttrycker är implikation: för vilka personer gäller det att "OM det finns en klädesbutik i stad X SÅ har personen stamkundskort vid butiken eller "OM Soini håller en kurs SÅ är studenten anmäld till den eller OM 40101 är anmäld till en viss kurs SÅ är flickan anmäld till samma kurs. Vi ska studera det sista exemplet i detalj här. Å ena sidan har vi en lista (en relation) med flickor och de kurser de är anmälda till: denna blir relation r. Å andra sidan har vi en lista (en relation) av ALLA de kurser som student 40101 tar. Denna blir relation s. Nu frågar vi vilka av flickorna som tar ALLA dessa kurser. Anta, att 40101 tar följande tre kurser:, och. För att då komma med till svaret måste flickan i fråga vara anmäld till alla dessa tre kurser. (Hon får också därtill vara anmäld till andra kurser, detta påverkar inte resultatet.) Frågan uttrycks med relationsalgebra (med exempeldatabasen kursdatabas som finns på kursens hemsida) som Π matr, namn, kurskod ( kön = 'K' (student) x kursanmälan) Π kurskod ( matr = 40101 (kursanmälan)) och resultatet består av en relation med attributen namn och matr. Attributet kurskod som vi dividerar med försvinner vid divisionen. OBS! Vilka personer har avklarat ALLA de kurser de är anmälda till? har ytligt sett en liknande formulering, men logiken här är annorlunda: mängden av kurser som ska testas varierar från person till person, beroende på vilka kurser just den aktuella personen har anmält sig till. Det finns alltså inte en gemensam mängd kurser som skulle testas för varje person. Formuleringen på SQL blir ofta lättare än med division; för att uttrycka exempelfrågan räcker det med att man ur mängden av kursanmälda personer tar bort dem som har NULL 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? Π matr, namn, kurskod ( kön = 'K' (student) x kursanmälan) Π kurskod ( matr = matr namn up kön kurskod matr kurskod ================================ =========== 40112 Brita DT K 40101 40112 Brita DT K 40101 40113 Ann Helen DT K 456304 40101 40113 Ann Helen DT K 40113 Ann Helen DT K 40128 Siru DT K 40128 Siru DT K 40128 Siru DT K 40240 Sara IS K 456304 40240 Sara IS K 40240 Sara IS K 40101 (kursanmälan)) Π matr, namn, kurskod ( kön = 'K' (student) x kursanmälan) Π kurskod ( matr = 40101 (kursanmälan)) matr namn up kön kurskod matr kurskod ================================ =========== 40112 Brita DT K 40101 40112 Brita DT K 40101 40113 Ann Helen DT K 456304 40101 40113 Ann Helen DT K 40113 Ann Helen DT K 40128 Siru DT K 40128 Siru DT K 40128 Siru DT K 40240 Sara IS K 456304 40240 Sara IS K 40240 Sara IS K Vi ser att bara 40128, Siru har alla de önskade kurserna.
r s definieras som Π R S (r) Π R S ((Π R S (r) s) Π R S,S (r)) r < Π matr, namn, kurskod ( kön = 'K' (student) x kursanmälan) s < Π kurskod ( matr = 40101 (kursanmälan)) Π R S (r) är här Π matr, namn (r) (Inga duplikat av element i mängder!) Π R S,S (r) är här Π matr, namn, kurskod (r), i detta fall = r. Projektionen garanterar att attributen kommer i rätt ordning med tanke på subtraktionen. Π R S (r) s är här Π matr, namn (r) x {,, }, dvs. alla de tänkbara kombinationerna av en kursanmäld flicka och de kurser som student 40101 tar. Ur denna mängd subtraherar vi de verkliga tuplerna bort. Ur detta resultat (de som inte är verkliga kombinationer) projiceras matrikelnummer och namn (Π R S ). Slutligen subtraheras dessa ur Π matr, namn (r). Svaret blir det som finns kvar. Π R S (r) Π R S (r) s (tänkbara) Π R S,S (r) (verkliga) ============== ===================== ===================== 40112 Brita 40112 Brita 40112 Brita 40113 Ann Helen 40112 Brita 40112 Brita 40128 Siru 40112 Brita 40113 Ann Helen 456304 40240 Sara 40113 Ann Helen 40113 Ann Helen 40113 Ann Helen 40113 Ann Helen SVAR: 40113 Ann Helen 40128 Siru ===== 40128 Siru 40128 Siru 40128 Siru 40128 Siru 40128 Siru 40128 Siru 40240 Sara 456304 40240 Sara 40240 Sara 40240 Sara 40240 Sara 40240 Sara De falska De tupler som representerar (De som inte markerats med kandidaterna verklig information har grönt representerar verklig har överstrukits. överstrukits. information om andra kurser än de relevanta.)
HUR ÖVERSÄTTA DETTA TILL MySQL? Problem: Det finns varken division ( ) eller mängddifferens ( ) i MySQL. Det gäller alltså att gå omvägar. Det finns (åtminstone) tre olika sätt att ställa dylika frågor: 1) Not exists ( ) med not in ( ): ( Det får inte finnas en kurs som 40101 tar som inte finns bland de kurser den aktuella flickan tar ) select distinct matr, namn from student as R where kon = 'K' and not exists (select kurskod where matr = 40101 and kurskod not in (select kurskod as R2 where R.matr = R2.matr)); matr namn 40128 Siru 1 row in set (0.00 sec) Den yttersta selektionen bestämmer vilka kolumner (matr, namn) vi vill se i svaret. Den yttersta select from where skapar tabellen med alla de kvinnliga studerandena och deras kursanmälningar. Dessa studeranden testas nu en i sänder: för att komma med i resultatet måste not exists satsen för den aktuella studenten bli true (listan efter not exists måste bli tom). Vad består denna lista av? Först väljer man dit alla de kurser som student 40101 är anmäld till (tre st.:, och ). Dessa representeras av kurskod. Sedan testas dessa kurser en i sänder mot de kurser som den aktuella kvinnliga studenten är anmäld till: vi skapar en mängd över hennes kurser i den innersta selecten. Dessa representeras av kurskod. Denna mängd innehåller alltså alla de kurser som den aktuella kvinnliga studenten är anmäld till. Så fort vi hittar en kurskod bland 40101:s kurser som inte finns bland den aktuella flickans kurskoder, får vi ett värde till den mellersta select listan, som härmed inte förblir tom. Detta medför att not exists blir false, och detta i sin tur innebär att den aktuella studenten inte fyller where villkoret och inte kommer med i resultatet. Om däremot alla kurskoderna i den mellersta selektionen går att återfinna bland den aktuella kvinnliga studentens kurskoder, förblir denna select tom och not exists blir därmed sann. Nu kommer den aktuella studenten med i resultatet. Se nästa sida!
I exempelfrågan: 40101:s kurser: Britas (40112) kurser: Ann Helens (40113) kurser: 456304 Sirus (40128) kurser: Saras (40240) kurser: 456304 visar vilken (vilka) kurs(er) som fattas av flickornas kursmängder. Dessa kommer med i den mellersta selektionen och gör att not exists blir false. Ifall alla 40101:s kurser går att å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 aktuella flickan ) select distinct matr, namn from student as R where kon = 'K' and not exists (select kurskod as S where matr = 40101 and not exists (select kurskod as R2 where R.matr = R2.matr and S.kurskod = R2.kurskod )); matr namn 40128 Siru 1 row in set (0.01 sec) Denna lösning påminner mycket om den förra, men nu har testen med mängdtillhörighet (not in) ersatts av en not exists till. Den yttre not exists väljer ut de kurser som 40101 är anmäld till. Den inre not exists kontrollerar dessa kurser en i sänder; hittar vi samma kurs för den aktuella flickan? Om det finns en kurskod (bland flickans kurser) lika med den kurskod (av 40101:s kurser) som står i tur att testas, kommer denna kurskod att vara resultatet av den innersta selecten. Då är inte denna select tom, och då blir den inre not exists false. Nu kommer den testade kurskoden inte med i den mellersta selektionen. Varje kurskod för 40101 testas på detta sätt. Om de alla går att hitta bland den aktuella flickans kurskoder, kommer ingen av dem med i den mellersta selektionen, och då blir den yttre not exists true. Detta innebär att den aktuella flickan har alla de specificerade kurserna och hon kommer med i resultatet. Om det däremot finns en eller flera kurskoder som inte går att finna i den innersta selektionen, blir denna select tom och därmed den inre not exists true för dem, och dessa kurskoder väljs till resultatet av den mellersta selektionen. Då blir den yttre not exists falskt, och den aktuella flickan (som saknade dessa kurser) kommer inte med i resultatet. Se figuren på nästa sida!
40101s kurser Britas (40112) kurser Hittas inte väljs i den mellersta selecten. Ann Helens (40113) kurser Hittas inte väljs i den mellersta selecten 456304 Sirus (40128) kurser Saras (40240) kurser Hittas inte väljs i den mellersta selecten 456304 Nästa sida!
3) Räkna och jämför Idén bakom denna metod är att räkna hur många olika värden det finns i divisorn (alla de där värdena som man borde ha för att komma med i resultatet). När det gäller vårt exempel räknar vi först hur många kurser 40101 tar. Sedan kontrollerar vi hur många av dessa specifika kurser våra kvinnliga studenter tar. Om det blir samma antal, har studentflickan alla dessa kurser och kommer med i resultatet. mysql> select matr, namn natural join student where kon = 'K' and kurskod in (select kurskod where matr = 40101) group by matr having count(kurskod) = (select count(*) from (select kurskod where matr = 40101) as tab); matr namn 40128 Siru 1 row in set (0.00 sec) Här finns resultatet av kursanmalan natural join student where kon = 'K' and kurskod in (select kurskod where matr = 40101): matr kurskod vitsord namn up kon 40112 NULL Brita DT K 40112 NULL Brita DT K 40113 NULL Ann Helen DT K 40113 NULL Ann Helen DT K 40128 NULL Siru DT K 40128 NULL Siru DT K 40128 NULL Siru DT K 40240 NULL Sara IS K 40240 NULL Sara IS K 9 rows in set (0.00 sec) Nästa sida!
Dessa tupler ska nu grupperas enligt matrikelnumret: matr kurskod vitsord namn up kon 40112 NULL Brita DT K 40112 NULL Brita DT K 40113 NULL Ann Helen DT K 40113 NULL Ann Helen DT K 40128 NULL Siru DT K 40128 NULL Siru DT K 40128 NULL Siru DT K 40240 NULL Sara IS K 40240 NULL Sara IS K having count(kurskod) räknar nu antalet kurskoder/grupp, dvs. antalet kurser varje studentflicka har anmält sig till (observera, att vi har valt till denna selektion bara sådana kurser som 40101 tar det är möjligt att flickorna tar andra kurser utöver dessa). having count(kurskod) = (select count(*) from (select kurskod where matr = 40101) as tab); jämför antalet kurser/grupp (antalet kurser den aktuella flickan tar) med antalet kurser 40101 tar (här 3 st.). Om det blir samma antal, vet vi att det är frågan om samma kurser, och då kommer flickan med i resultatet. Detta händer i den selektion som finns i början av frågan: mysql> select matr, namn natural join student where kon = 'K' and kurskod in (select kurskod where matr = 40101) group by matr having count(kurskod) = (select count(*) from (select kurskod where matr = 40101) as tab); OBS! Här kan det bara förekomma en kursanmälan/kurs. I andra situationer måste man kanske använda count distinct för att eliminera duplikat. OBS! Det skulle vara en naturlig lösning att använda en temporär tabell för 40101:s kurser. Tyvärr får samma temporära tabell INTE öppnas två gånger i samma fråga i MySQL (5.1) så den lösningen ger bara felmeddelanden.