Avatar billede Slettet bruger
16. december 2005 - 15:51 Der er 12 kommentarer og
2 løsninger

Fejl i JOIN med GROUP BY

Hej,

Jeg har problemer med denne forespørgsel. Efter min mening skulle det være underordnet om der bliver brugt INNER JOIN eller LEFT JOIN, da der altid vil være et felt i [Testing_1] jf der ingen kriterier for denne tabel er. Men INNER JOIN giver færre resultater end LEFT JOIN - er der nogen tyrk-fejl eller grundlæggende fejltrin i denne forespørgsel?

SELECT
    MIN([testing_1].[testing started]) AS [Testing started],
FROM ([testing]
LEFT JOIN [testing] AS [testing_1] ON (
    TRIM([testing].[Product]) = TRIM([testing_1].[Product]) AND
    TRIM([testing].[Type]) = TRIM([testing_1].[Type]) AND
    TRIM([testing].[TOM TR name]) = TRIM([testing_1].[TOM TR name]) AND
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing') = IIF(TRIM(UCASE([testing_1].[Test platform]))='REPORT','Report','Testing') AND
    TRIM(UCASE([testing].[round])) = TRIM(UCASE([testing_1].[round]))))
WHERE TRIM(UCASE([testing].[Status])) NOT IN ('DONE','CLOSED','CANCELLED')
GROUP BY
    TRIM([testing].[Product]),
    TRIM([testing].[Type]),
    TRIM([testing].[TOM TR name]),
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing'),
    TRIM(UCASE([testing].[round])),


Hvis det skulle gøre nogen forskel er her hele forespørgslen.



SELECT
    TRIM([testing].[Product]) AS [Product],
    TRIM([testing].[Type]) AS [Type],
    TRIM([testing].[TOM TR name]) AS [TOM TR name],
    TRIM(UCASE([testing].[round])) AS [Round],
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing') AS [Test platform],
    MAX([testing].[Test request description]) AS [Test request description],
    MAX([testing].[TCC Target date]) AS [TCC Target date],
    MIN([testing].[TCC Target date]) AS [TCC Target date min],
    MAX([testing].[PP demand date]) AS [PP demand date],
    SUM([testing].[remaining]) AS [Remaining],
    COUNT(*) AS [Number of test platforms],
    MIN([testing_1].[testing started]) AS [Testing started],
    [comment].[date] AS [comment_date],
    TRIM([comment].[user] & ' - ' & UCASE([testing].[test platform])) AS [comment_user],
    TRIM([comment].[comment]) AS [comment]
FROM ([testing]
LEFT JOIN [testing] AS [testing_1] ON (
    TRIM([testing].[Product]) = TRIM([testing_1].[Product]) AND
    TRIM([testing].[Type]) = TRIM([testing_1].[Type]) AND
    TRIM([testing].[TOM TR name]) = TRIM([testing_1].[TOM TR name]) AND
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing') = IIF(TRIM(UCASE([testing_1].[Test platform]))='REPORT','Report','Testing') AND
    TRIM(UCASE([testing].[round])) = TRIM(UCASE([testing_1].[round]))))
LEFT JOIN [comment] ON [Comment].[referid] = [testing].[id]
WHERE TRIM(UCASE([testing].[Status])) NOT IN ('DONE','CLOSED','CANCELLED')
GROUP BY
    TRIM([testing].[Product]),
    TRIM([testing].[Type]),
    TRIM([testing].[TOM TR name]),
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing'),
    TRIM(UCASE([testing].[round])),
    [comment].[date],
    TRIM([comment].[user] & ' - ' & UCASE([testing].[test platform])),
    TRIM([comment].[comment])
ORDER BY
    TRIM([testing].[Product]),
    TRIM([testing].[Type]),
    TRIM([testing].[TOM TR name]),
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing'),
    TRIM(UCASE([testing].[round])),
    [comment].[date] DESC

Vh Mathias.
Avatar billede Slettet bruger
16. december 2005 - 16:02 #1
Jeg fjerner lige de kommer, jeg ikke fik fjernet, da jeg forenklede forespørgslen.

SELECT
    MIN([testing_1].[testing started]) AS [Testing started]
FROM ([testing]
LEFT JOIN [testing] AS [testing_1] ON (
    TRIM([testing].[Product]) = TRIM([testing_1].[Product]) AND
    TRIM([testing].[Type]) = TRIM([testing_1].[Type]) AND
    TRIM([testing].[TOM TR name]) = TRIM([testing_1].[TOM TR name]) AND
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing') = IIF(TRIM(UCASE([testing_1].[Test platform]))='REPORT','Report','Testing') AND
    TRIM(UCASE([testing].[round])) = TRIM(UCASE([testing_1].[round]))))
WHERE TRIM(UCASE([testing].[Status])) NOT IN ('DONE','CLOSED','CANCELLED')
GROUP BY
    TRIM([testing].[Product]),
    TRIM([testing].[Type]),
    TRIM([testing].[TOM TR name]),
    IIF(TRIM(UCASE([testing].[Test platform]))='REPORT','Report','Testing'),
    TRIM(UCASE([testing].[round]))
Avatar billede imago-dei Nybegynder
16. december 2005 - 16:17 #2
Hvis du får to forskellige resultater og det eneste du ændrer er en left join til en inner join, så er det fordi at du har null values i din venstre tabel i en af de kolonner som du joiner på (Product, type, TOM TR name osv). Men ellers kan jeg ikke forstå hvorfor du joiner på alle de kolonner. Har du ikke en nøgle du kan joine på? Eller består nøglen af alle de kolonner?
Avatar billede kjulius Novice
16. december 2005 - 16:45 #3
Jeg tror du har en null værdi i et eller flere af de felter du joiner på. Selv om der jo vil være null i begge rows, da det er den samme tabel du joiner sammen, vil en inner join alligevel ignorere dem, da der ikke kan joines på en null-værdi. Derimod vil en LEFT JOIN medtage en sådan row.

Prøv evt. at teste på null, og omdanne den til en blank i stedet (med en IIF) i dine ON conditioner.
Avatar billede Slettet bruger
16. december 2005 - 16:49 #4
skal jeg så lave ([testing].[felt] = [testing_1].[felt] OR (ISNULL([testing].[felt]) AND ISNULL([testing_1].[felt]))) på alle 5 felter?

Jeg skal have den højeste [testing started] i hver GROUP (og dette værdi skal findes uden kriteriet) og nøglen er derfor de 5 kolonner.
Avatar billede kjulius Novice
16. december 2005 - 17:12 #5
Jeg tænkte mere på at omdanne evt. null værdier til en slags dummy-værdi før du joiner (null indikerer jo, at feltet intet indeholder). På den måde kan du "snyde" din inner join.

Noget lignende:

LEFT JOIN [testing] AS [testing_1] ON (
    TRIM(IIF([testing].[Product] IS NULL, ' ', [testing].[Product])) = TRIM(IIF([testing_1].[Product] IS NULL, ' ', [testing_1].[Product])) AND
    TRIM(IIF([testing].[Type] IS NULL, ' ', [testing].[Type])) = TRIM(IIF([testing_1].[Type] IS NULL, ' ', [testing_1].[Type])) AND
    TRIM(IIF([testing].[TOM TR name] IS NULL, ' ', [testing].[TOM TR name])) = TRIM(IIF([testing_1].[TOM TR name] IS NULL, ' ', [testing_1].[TOM TR name])) AND
    IIF(TRIM(UCASE(IIF([testing].[Test platform] IS NULL, ' ', [testing].[Test platform])))='REPORT','Report','Testing') = IIF(TRIM(UCASE(IIF([testing_1].[Test platform] IS NULL, ' ', [testing_1].[Test platform])))='REPORT','Report','Testing') AND
    TRIM(UCASE(IIF([testing].[round] IS NULL, ' ', [testing].[round]))) = TRIM(UCASE(IIF([testing_1].[round] IS NULL, ' ', [testing_1].[round]))))

Jeg har som jeg foreslog erstattet null med en blank. Hvis feltet er numerisk, bør det erstattes med en dummy numerisk værdi i stedet.

Jeg vil tro, at du efter denne ændring vil opdage, at der ikke længere er forskel på en inner join og en left outer join.
Avatar billede Slettet bruger
16. december 2005 - 17:22 #6
Hvad med at lave en forespørgsel først med brug af:

Felt: nz([Dit felt];0)

og så køre din forespørgsel!~)
Avatar billede kjulius Novice
16. december 2005 - 17:37 #7
--> spg: Tja, i bund og grund er det vel det samme. Men du har da ret. Man kan lave en sådan grundlæggende forespørgsel, hvor man omdanner felters evt. null-værdier til en dummy-værdi, som dog helst skal være unik i forhold til feltets øvrige værdier.
Herefter kan man bruge denne forespørgsel som udgangspunkt for sin join.

Du har brugt funktionen nz, hvor jeg har brugt iif til at teste for null. Jeg tilstår, at nz nok er mere elegant, men jeg bruger den sjældent...

Hvis spørgeren skulle ønske at bruge en sådan to-trins-raket, vil det desuden være oplagt også at indlægge alle de øvrige konverteringer i trin 1 (TRIM, UCASE osv). Det ville gøre den efterfølgende forespørgsel noget nemmere.
Avatar billede Slettet bruger
16. december 2005 - 18:16 #8
Great,

Problemer var egentlig ikke at JOINsne ikke var ens. Men LEFT JOIN udelader også RIGHT table values hvis LEFT table er NULL (har jeg kunnet erfare fra denne tråd - ret mig hvis jeg tager fejl). Jeg skulle bruge alle JOINs, og det får jeg nu..

Smid et svar begge to.

Mathias.
Avatar billede imago-dei Nybegynder
17. december 2005 - 14:46 #9
Din sidste kommentar kan jeg ikke rigtig forstå. Men jeg vil gerne høre lidt mere om hvordan du løste problemet, og hvad var galt.

Inner, left og right joins foregår således:

En Inner join medtager alle rækker i begge tabeller, hvor alle join kriterier matcher.

En left (outer) join medtager alle rækker i venstre tabel, og de rækker i højre tabel, hvor join kriterierne matcher.

En right (outer) join medtager alle rækker i højre tabel, og de rækker i venstre tabel, hvor join kriterierne matcher.
Avatar billede kjulius Novice
17. december 2005 - 18:27 #10
Hmm... Nej, jeg forstår heller ikke rigtigt den kommentar, med mindre der med RIGHT TABLE menes den tabel, som angives på højre side af en LEFT JOIN.

Sagen er jo, at man ikke kan bruge en null værdi til at joine rows i to tabeller. Null anses altså ikke for at være lig med null i en sammenligning. Det kan lyde forkert, men på den anden side, så angiver null jo, at der ikke er nogen værdi. Det ville jo også være forkert at sige at intet = intet. Hvordan kan man sammenligne noget som ikke er der? Det er forøvrigt en regel som ikke kun gælder i SQL, men i alle programmeringssprog som understøtter begrebet null.
Avatar billede kjulius Novice
17. december 2005 - 18:33 #11
Men godt du fik løst dit problem, og hvis du fik klaret begreberne lidt, så endnu bedre...
Avatar billede Slettet bruger
18. december 2005 - 00:19 #12
Jeg kendte nu godt de forskellige JOIN typer, men var ikke klar over at man ikke i en JOIN kan JOINe Null values (var det jeg uden succes forsøgte at forklare i min tidligere kommentar :c)).

Imago>> smid venligts et svar.
Avatar billede imago-dei Nybegynder
19. december 2005 - 09:09 #13
Her er et svar.

Til kjulius. Du har ret i, at null ikke må være i join kriterierne. Null != null. Men i en left outer join, tages ALLE rækker med fra venstre tabel. Venstre tabel er den tabel som nævnes først.

SELECT * FROM VenstreTabel LEFT OUTER JOIN HojreTabel ON VenstreTabel.Id = HojreTabel.Id.
Avatar billede kjulius Novice
20. december 2005 - 00:14 #14
--> imago-dei: Ja, det er jeg klar over. Det er beklageligt, hvis jeg ikke har formået at lade det skinne igennem! ;-) Må vist hellere få mig tilmeldt det skrivekursus, jeg har overvejet...
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
Dyk ned i databasernes verden på et af vores praksisnære Access-kurser

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