webForumDet fria alternativet

Hjälp sökes för UNION

Databaser & SQL

4 svar · 1 219 visningar · startad av thodah

Medlem sedan apr. 20112 inlägg
Frågan#1

Jag har haft stora problem med en otrolig kurs i databas som är det enda jag har kvar för att få ut min examen. I uppgiften har jag satt upp en databas i Access för en skola och på denna fråga jag fått rest på ska jag:

"Visa antal kurstillfällen per kurs under 2008! Samtliga kurser skall finnas med."

Jag har löst det med en UNION på detta sätt:

SELECT Kursnamn, COUNT(*) AS Antal
FROM Kurstillfälle
WHERE Datum BETWEEN CDate('2008-01-01') AND CDate('2008-12-31')
GROUP BY Kursnamn
UNION SELECT DISTINCT namn, 0
FROM Kurs, Kurstillfälle
WHERE Kurstillfälle.Datum BETWEEN CDate('2008-01-01') AND CDate('2008-12-31')
AND Kurs.namn NOT IN (SELECT kursnamn FROM Kurstillfälle);

SQL-satsen verkar ta fram rätt information, men läraren skriver "Vad gör den undre delen av union? Kolla era villkor." Så vad har jag missat? Jag använder Microsoft Access 2007 och bifogar en bild på databasens relationer.

access-relationships-thodah.png
Medlem sedan dec. 200012 464 inlägg
#2

Hej och välkommen.

Din bild är helt oläslig och de flesta tabellerna är ju helt irrelevanta för denna fråga. Det vore bättre om du enbart beskrev vilka kolumner som ingår i tabellerna kurs och kurstillfälle.

Det fel som finns i din fråga är att datumvillkoret ligger fel. Du vill ha namnet på de kurser som inte gavs 2008. Det du hämtar är de kurser som aldrig har haft något kurstillfälle.

Hela frågan kan för övrigt skrivas utan union genom att använda en outer join.

Medlem sedan apr. 20112 inlägg
#3

Vad konstigt, den bifogade bilden ser bra ut på min dator. Jag bifogar en ny där jag har ringat in de berörda tabellerna och markerat kolumnerna som är intressanta för frågan. Det är alltså tabellen "Kurs" med kolumnen "namn" och tabellen "Kurstillfälle" med kolumnen "datum" som frågan gäller för.

Vad konstigt för när jag kör frågan får jag ändå "Kurs A: 1, Kurs B: 0, Kurs C: 3" och så vidare, så det blir ju svaret rätt på frågan hur många kurstillfällen varje kurs haft. Jag tittade i tabellen för "Kurstillfälle" och där stämmer antal tillfällen för 2008 överens med de vid körning av SQL-frågan.

I min SQL-sats har jag väl tänkt att först visas alla kurser som haft kurstillfällen med eras antal, sedan med UNION sätts det ihop med de kurser som inte haft några kurstillfällen och visas med siffran 0.

access-relationships-inringat-thodah.png
Medlem sedan aug. 20039 340 inlägg
#4

Frågan är lite konstigt ställd. Man ska visa alla kurstillfällen under 2008. Ok, det kan jag köpa. Sedan ska samtliga kurser finnas med. Varför då? Om man följer frågan slaviskt så får man alltså (kanske) en hög kurser med 0 kurstillfällen, alltså kurser som inte har några kurstillfällen under 2008.

Men om det nu är det som efterfrågas så behöver du en LEFT JOIN!

SELECT    k.namn, COUNT(kt.kursID)
FROM      kurs k
LEFT JOIN kurstillfälle kt ON k.namn = kt.kursnamn 
          AND kt.Datum BETWEEN CDate('2008-01-01') AND CDate('2008-12-31')
WHERE     1
GROUP BY  k.namn

Vad som sker är ungefär följande. (innan GROUP BY och COUNT)

[B]k.namn      kt.kursID[/B]
Kurs 1      0
Kurs 1      1
Kurs 1      2

Kurs 2      3

Kurs 3      4
Kurs 3      5

Kurs 4      null

Kurs 5      6

Förklaring: Kurs 1 har i detta exempel tre tillfällen, med id 0, 1, 2. Kurs 2 har ett tillfälle. Kurs 3 har två. Kurs 4 har inget, vilket gör att LEFT JOIN producerar en rad med null-värden för alla kt's värden. (En INNER JOIN hade istället utelämnat kurs 4) Kurs 5 har slutligen ett tillfälle. Det är runda ett.

Runda två är GROUP BY och beräkning av aggregatsfunktionen COUNT.

[B]k.namn      kt.kursID | k.namn      COUNT(kt.kursID) [/B]
Kurs 1      0         | Kurs 1      3
Kurs 1      1         |
Kurs 1      2         |
                      |
Kurs 2      3         | Kurs 2      1
                      |
Kurs 3      4         | Kurs 3      2
Kurs 3      5         |
                      |
Kurs 4      null      | Kurs 4      0
                      |
Kurs 5      6         | Kurs 5      1

Det är inga direkta konstigheter utöver Kurs 4. COUNT ignorerar nämligen null-rader, just för att man ska kunna utföra denna typ av beräkningar! Alltså får du att kurs 4 inte har några tillfällen (inom det specificerade perioden.)

Medlem sedan dec. 200012 464 inlägg
#5
SELECT DISTINCT namn, 0
  FROM Kurs, Kurstillfälle
 WHERE Kurstillfälle.Datum BETWEEN Date'2008-01-01' AND Date'2008-12-31'
   AND Kurs.namn NOT IN (SELECT kursnamn FROM Kurstillfälle);

Om du tittar på sista villkoret så får du bara med de kurser som aldrig har getts. Du får inte med kurser givna något annat år som inte givits under 2008

med följande testmaterial

create table kurs (namn char(20) primary key);
create table kurstillfälle(kursnamn char(20), datum date);

insert into kurs values('A');
insert into kurs values('B');
insert into kurs values('C');

insert into kurstillfälle values('A',CDate('2008-12-24'));
insert into kurstillfälle values('A',CDate('2008-12-25'));
insert into kurstillfälle values('A',CDate('2007-12-25'));

insert into kurstillfälle values('B',CDate('2007-12-24'));

så ger din fråga

SELECT Kursnamn, COUNT(*) AS Antal
  FROM Kurstillfälle
 WHERE Datum BETWEEN Date'2008-01-01' AND Date'2008-12-31'
 GROUP BY Kursnamn
UNION 
SELECT DISTINCT namn, 0
  FROM Kurs, Kurstillfälle
 WHERE Kurstillfälle.Datum BETWEEN Date'2008-01-01' AND Date'2008-12-31'
   AND Kurs.namn NOT IN (SELECT kursnamn FROM Kurstillfälle);

resultatet

kursnamn                   Antal
========                   =====
A                              2
C                              0

Du får alltså inte med kursen B.

Jämför med denna fråga

SELECT Kursnamn, COUNT(*) AS Antal
  FROM Kurstillfälle
 WHERE Datum BETWEEN CDate('2008-01-01') AND CDate('2008-12-31')
 GROUP BY Kursnamn
UNION all
SELECT namn, 0
  FROM Kurs
 WHERE Kurs.namn NOT IN 
      (SELECT kursnamn 
         FROM Kurstillfälle
        WHERE Datum BETWEEN CDate('2008-01-01') 
                                   AND CDate('2008-12-31'));

Kommentaren om bilden var mer tänkt inför kommande poster :)

262 ms totalt · 4 externa anrop · v20260731065814-full.86ec41c2
127 ms — deklarationer (db)
0 ms — hämta statistik (cache)
133 ms — hämta tråd, inlägg och bilagor (db)
127 ms — ändringar (db)