Avatar billede el_fredo Praktikant
28. november 2003 - 11:37 Der er 24 kommentarer og
2 løsninger

Vis indhold på trods af manglende forekomst

Jeg har en sql-sætning. Følgende er en stump heraf, som ikke melder syntaxfejl, men som stadigvæk har fejl.

SELECT medarb.nr, medarb.navn, gruppe1.timer
FROM (SELECT DISTINCT sagspost.nr, medarbejder.medarbejder_navn AS navn
FROM sagspost, medarbejder
WHERE sagspost.posteringsdato BETWEEN '2000.01.01' AND '2003.10.31' AND sagspost.nr = medarbejder.nr) medarb INNER JOIN (SELECT sagspost.nr, SUM sagspost.antalregistreret) timer
FROM sagspost
WHERE sagspost.posteringsdato BETWEEN '2000.01.01' AND '2003.10.31' AND (sagspost.aktivitetsnummer BETWEEN '080' AND '189')
GROUP BY sagspost.nr) gruppe1 ON medarb.nr = gruppe1.nr

Hvis sætningen er for kompliceret, så er det heller ikke sikkert, at den er nødvendig at kende, for at løse mit problem. Problemet er:

Hvis en medarbejder ikke har en time-forekomst indenfor aktivitetsgruppe 080 og 189 som anført i sql'en ovenfor, så bliver denne medarbejder helt udelukket fra visningen. Det skal han/hun ikke. Derudover, når jeg senere kobler flere aktivitetsgrupper på, så forsvinder der endnu flere medarbejdere, hvis ikke de har et aktivitetsnummer i alle intervallerne. Så til sidst, når jeg har alle 5 grupper af aktivitetsnumre slået til, så er ikke en eneste medarbejder vist, da der ikke er en eneste af dem, der har timer indenfor alle områderne. Jeg vil gerne have, at alle medarbejdere bliver vist OGSÅ selvom de ikke har timer i et interval. Så skal der bare stå "0".

Jeg har hentet data fra en Oracle-database, og der kan jeg benytte syntaxen "(+)" for at inkludere grupper, også selvom de er tomme. Hvad er den tilsvarende syntax i SQL-server?
Avatar billede venne Nybegynder
28. november 2003 - 11:45 #1
Den tilsvarende syntaks i MSSQL er:

SELECT...
FROM a LEFT JOIN b
  ON a.a_id = b.fk_a_id
WHERE...

Her selekteres alle forekomster i a og de tilhørende forekomster i b, hvis der er nogen, ellers NULL.
Avatar billede trer Nybegynder
28. november 2003 - 11:53 #2
Du skal benytte enten en LEFT OUTER JOIN eller en RIGHT OUTER JOIN i ovenstående tilfælde. I ekstreme tilfælde kan du være nød til at lave en FULL OUTER JOIN.

Bemærk i øvrigt, at når du laver OUTER JOINS bliver brug af indeks ekstremt dårlig - ofte vil du ende med rene tablescans, hvilket er langsomt.

I øvrigt: Er du i en situation hvor du ender med multiciple OUTER JOINS i samme statement er det typisk et tegn på, at du ikke har fået gennemtænkt dit udtræk. I de tilfælde hvor det er nødvendigt, kan det ofte betale sig at vurdere effekten af en FULL OUTER JOIN eller en CROSS JOIN kombineret med en eller flere begrænsende INNER JOINS.

Det er i øvrigt mere korrekt og læsevenligt at bruge den fulde syntaks fremfor blot JOIN og LEFT JOIN / RIGHT JOIN.
Avatar billede el_fredo Praktikant
28. november 2003 - 11:56 #3
Hvad så når jeg har flere end 2? Skriver jeg så:
SELECT...
FROM a LEFT JOIN b, c, d
  ON a.id = b.id
  AND
  a.id = c.id
  AND
  a.id = d.id

eller? Det ser forkert ud.
Avatar billede trer Nybegynder
28. november 2003 - 11:58 #4
select ...
from a left outer join b on a.id = b.id
left outer join c on a.id = c.id
left outer join d on b.id = d.id
etc...
Avatar billede el_fredo Praktikant
28. november 2003 - 12:30 #5
Skal jeg bruge LEFT OUTER JOIN eller LEFT JOIN?
Avatar billede trer Nybegynder
28. november 2003 - 12:33 #6
LEFT OUTER JOIN er den korrekte fulde syntaks, LEFT JOIN er en forkortelse hvor OUTER er underforstået, ligesom JOIN er en forkortelse for INNER JOIN.
Avatar billede el_fredo Praktikant
28. november 2003 - 12:35 #7
Jeg kan ikke finde ud af, at passe en LEFT OUTER JOIN ind i sql-sætningen. Jeg får en fejl, hvis jeg skriver:

sql="SELECT medarb.nr, medarb.navn, gruppe1.timer FROM "+
                "(SELECT DISTINCT sagspost.nr, medarbejder.medarbejder_navn FROM "+
                "sagspost, medarbejder WHERE "+
                "sagspost.posteringsdato BETWEEN '01-01-2000' AND '12-31-2003' "+
                "AND "+
                "sagspost.nr = medarbejder.nr) medarb LEFT JOIN "+
                "(SELECT sagspost.nr, SUM sagspost.antalregistreret) timer FROM sagspost WHERE "+
                "sagspost.posteringsdato BETWEEN '01-01-2000' AND '12-31-2003' "+
                "AND "+
                "(sagspost.aktivitetsnummer BETWEEN 80 AND 189) "+
                "GROUP BY sagspost.nr) gruppe1 ON medarb.nr = gruppe1.nr";

Jeg bruger C# som programmeringssprog.
Avatar billede el_fredo Praktikant
28. november 2003 - 12:36 #8
VRÆÆÆÆÆÆÆÆL. Hvad gør jeg galt?
Avatar billede trer Nybegynder
28. november 2003 - 12:58 #9
Brug Query Analyzer til at skrive din SQL - det er væsentligt bedre end Visual Studio. Når din SQL så er færdig - så kopier den ind i dit C# program.

Derudover - du har problemer med de parenteser du indsætter.

I øvrigt er det altid at foretrække at skrive SQL'en som en stored procedure og så kalde den fra programmet.
Avatar billede el_fredo Praktikant
28. november 2003 - 13:32 #10
Ja, det er rigtigt. Jeg så godt den med parantesen efter SUM.
Jeg ved ikke helt hvad du mener med Stored Procedure... jeg redigerer jo SQL-sætningen dynamisk alt efter brugerens indtastning.
Avatar billede el_fredo Praktikant
28. november 2003 - 13:38 #11
Query Analyzer... er det et gratis prg?
Avatar billede el_fredo Praktikant
28. november 2003 - 13:39 #12
Ahhh. download.com
Avatar billede el_fredo Praktikant
28. november 2003 - 13:43 #13
Næh, den var til Pocket PC's. Har du et link til den rigtige?
Avatar billede trer Nybegynder
28. november 2003 - 13:49 #14
Således

create procedure spSomething (
    @startdato datetime, @slutdato datetime, @aktstart int, @aktslut int
)
as
SELECT medarb.nr, medarb.navn, gruppe1.timer
FROM (SELECT DISTINCT sagspost.nr, medarbejder.medarbejder_navn FROM "+
      sagspost inner join medarbejder
    on sagspost.nr = medarbejder.nr
    WHERE sagspost.posteringsdato BETWEEN @startdato AND @slutdato
    ) medarb
    LEFT outer JOIN
    (SELECT sagspost.nr, SUM sagspost.antalregistreret timer
    FROM sagspost
    WHERE sagspost.posteringsdato BETWEEN @startdato AND @slutdato
    AND (sagspost.aktivitetsnummer BETWEEN @aktstart AND @aktslut)
    GROUP BY sagspost.nr) gruppe1 ON medarb.nr = gruppe1.nr
go

kald

recordset.open "execute spSomething " & cstr(date1) & "," & cstr(date2) & ", " & cstr(akt1) & "," & cstr(akt2)

det giver både bedre performance / lavere serverbelastning + forbedrer sikkerheden idet du kun skal give din bruger ret til at kalde proceduren.

grant execute on spSomething to brugernavn
Avatar billede el_fredo Praktikant
28. november 2003 - 14:02 #15
Det betyder såmænd ikke noget. Det hele kører automatisk på serveren. Brugeren får jo kun det at se, som queryen returnerer. Det er i hvert tilfælde ikke så vigtigt lige nu. Det helt essentielle er, at jeg kan trække al data ud - også når der ikke står noget under en medarbejder. Så skal der bare stå "0".
Avatar billede el_fredo Praktikant
28. november 2003 - 14:18 #16
Så tak for eksemplet, men har du ikke mulighed for i stedet at vise mig hvordan jeg får alle medarbejdere med? Jeg har lavet en LEFT OUTER JOIN nu, og den virker fint. Nu skal jeg bare lige have testet den med 2 grupper.
Avatar billede el_fredo Praktikant
28. november 2003 - 14:27 #17
Nu får jeg fejl. Jeg synes ellers ikke at det ser forkert ud, men hvad ved jeg. Det ser nu sådan ud:

sql=""+
            "SELECT        medarb.nr, medarb.medarbejder_navn, gruppe1.timer, gruppe2.timer "+
            "FROM        (SELECT DISTINCT sagspost.nr, medarbejder.medarbejder_navn "+
                        "FROM    sagspost, medarbejder "+
                        "WHERE    sagspost.posteringsdato BETWEEN '1950.01.01' AND '2005.10.31' AND sagspost.nr = medarbejder.nr) medarb LEFT OUTER JOIN "+
                            "(SELECT    sagspost.nr, SUM(sagspost.antalregistreret) timer "+
                            "FROM        sagspost "+
                            "WHERE        sagspost.posteringsdato BETWEEN '1950.01.01' AND '2005.10.31' AND (sagspost.aktivitetsnummer BETWEEN 80 AND 189) "+
                            "GROUP BY sagspost.nr) gruppe1 ON medarb.nr = gruppe1.nr "+
                            "(SELECT    sagspost.nr, sum(sagspost.antalregistreret) timer "+
                            "FROM        sagspost "+
                            "WHERE        sagspost.posteringsdato BETWEEN '1950.01.01' AND '2005.10.31' AND (sagspost.aktivitetsnummer BETWEEN 190 AND 199) "+
                            "GROUP BY sagspost.nr) gruppe2 ON medarb.nr = gruppe2.nr";

What to do now? Laver jeg en forket join?
Avatar billede el_fredo Praktikant
28. november 2003 - 14:28 #18
Error near 'SELECT' - mere får jeg ikke at vide... der er jo 3 steder hvor der står SELECT...
Avatar billede trer Nybegynder
28. november 2003 - 15:03 #19
Her vil jeg tro - der mangler et join kriterie:

gruppe1 ON medarb.nr = gruppe1.nr "+
                            "(SELECT    sagspost.nr, sum(sagspost
Avatar billede el_fredo Praktikant
28. november 2003 - 15:07 #20
Hmm. Hvordan laver jeg det så?
Avatar billede el_fredo Praktikant
28. november 2003 - 15:08 #21
jeg er ikke så god til sql-prog.
Avatar billede el_fredo Praktikant
28. november 2003 - 15:08 #22
Jeg smider gerne 100 pts oveni hatten hvis det virker.
Avatar billede el_fredo Praktikant
28. november 2003 - 15:10 #23
JEG FIK DET TIL AT VIRKE!!!
Avatar billede el_fredo Praktikant
28. november 2003 - 15:11 #24
Det var en join jeg manglede. Tak for hjælpen begge to.
Avatar billede trer Nybegynder
28. november 2003 - 15:16 #25
Tillykke :-)
Avatar billede el_fredo Praktikant
28. november 2003 - 15:29 #26
Nu virker det hele. Og det var bare at skrive LEFT OUTER JOIN i en stor pærevælling... ahhh. nu bliver det en god weekend.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Computerworld tilbyder specialiserede kurser i database-management

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester