Ograniczenie do jednego elementu przy grupowaniu

0

Cześć,
poniżej wklejam zapytanie, którego celem jest wydobycie pewnych danych z bazy (szczegóły poniżej).
Tak ogólnie to mam 3 główne tabele, gdzie:
cdn.vatnag - przechowywane są dane nagłówkowe dokumentu (faktury)
cdn.vattab - przechowywane są pozycje faktury z tym, że każda pozycja składa się z dwóch kategorii (kategoria i kategoria2)
cdn.kategorie - tabela z kategoriami (kod/nazwa, opis itp).

Problem jest taki, że kategoria2 nie musi być zawsze uzupełniona (może być NULL).
Szukam sposobu, by tak zapisać zapytanie, by otrzymać sumę kwot dla każdej kombinacji kategorii oddzielnie tzn. jeśli obie kategorie są uzupełnione to potrzebuję sumę dla każdej występującej kombinacji tych obu kategorii. Natomiast jak jest jedna kategoria uzupełniona (bez kategoria2) to potrzebuję sumę dla wszystkich pozycji z podziałem na pierwszą kategorię (bez uwzględniania pozycji, gdzie jest ustawiona druga kategoria).
Mam nadzieję, że w miarę jasno to opisałem.

Problem jest taki, że jak w warunku wstawię samo

(...) AND (VAT_KAT2ID = B.KAT_KATID OR VAT_KAT2ID is Null) AND (...)

to otrzymuję sumę dla każdej możliwej kategorii (iloraz kartezjański). Musze to ograniczyć tylko do jednej kategorii. Wymyśliłem, że dodam:

AND B.KAT_KATID = 1 

Ale może się zdarzyć, że kategoria o takim ID zostanie wykasowana...
Szukam więc alternatywy.

Proszę więc o pomoc w konstrukcji zapytania zgodnie z opisem powyżej - tj. by otrzymać sumę kwot dla każdej występującej kombinacji kategorii (dodatkowo z podziałem na okres).
Z góry dziękuję za pomoc!

Kod całego zapytania:

SELECT 
	VAN_PODMIOTTYP AS PODMIOTU_TYP, 
	VAN_PODID AS PODMIOT_ID, 
	A.KAT_KODSZCZEGOL AS KATEGORIA,
	CASE 
		WHEN VAT_KAT2ID is Null THEN 'NULL'
		WHEN VAT_KAT2ID is not Null THEN B.KAT_KODSZCZEGOL
		END AS KATEGORIA2,
	A.KAT_OPIS AS OPIS,
	100*YEAR(VaN_DataWys)+MONTH(VaN_DataWys) AS OKRES, 
	VaN_KntNazwa1 AS PODMIOT_NAZWA, 
	sum(vat_netto) AS SUMA 
FROM 
	cdn.vattab, 
	cdn.vatnag, 
	CDN.KATEGORIE A,
	CDN.KATEGORIE B,
	CDn.RptZaznaczenia2
WHERE
	vat_vanid = van_vanid AND 
	VAT_KATID = A.KAT_KATID AND 
	(VAT_KAT2ID = B.KAT_KATID OR
	VAT_KAT2ID is Null AND B.KAT_KATID = 1) AND --Jak zastąpić B.KAT_KATID = 1 ???
	rpz2_GIDNumer = van_Vanid AND
	(??_NQCDN_FiltrApp) --to jest filtr aplikacji
GROUP BY 
	A.Kat_KodSzczegol,
	CASE 
		WHEN VAT_KAT2ID is Null THEN 'NULL'
		WHEN VAT_KAT2ID is not Null THEN B.KAT_KODSZCZEGOL
	END,
	A.KAT_OPIS, 
	VaN_PodmiotTyp, 
	VaN_PodID, 
	VaN_KntNazwa1, 
	100*YEAR(VaN_DataWys)+MONTH(VaN_DataWys)
ORDER BY 
	VaN_PodmiotTyp, VaN_PodID, 100*YEAR(VaN_DataWys)+MONTH(VaN_DataWys)
2

W ten sposób zapisy join to jest zawsze inner join, jeżeli dobrze rozumiem to musisz to zapisać tak:

FROM 
    cdn.vattab on -warunek
    inner join cdn.vatnag on --warunek
    inner join CDN.KATEGORIE A on  cdn.vatnag.VAT_KATID = A.KAT_KATID
   -- tutaj musisz miec left join skoro wiesz że nie masz dopasowań do wszystkich
    left join CDN.KATEGORIE B on VAT_ cdn.vatnag.KAT2ID = B.KAT_KATID
    inner join CDn.RptZaznaczenia2on --warunek

0

Dziękuję za pomoc.
Po zmianie na poniższy kod działa jak należy:

(...)
FROM
	cdn.vatnag
INNER JOIN cdn.vattab ON vat_vanid = van_vanid
INNER JOIN CDN.KATEGORIE A ON VAT_KATID = A.KAT_KATID
LEFT JOIN CDN.KATEGORIE B ON VAT_KAT2ID IS NOT NULL AND VAT_KAT2ID = B.KAT_KATID
INNER JOIN CDn.RptZaznaczenia2 ON rpz2_GIDNumer = van_Vanid

WHERE
	(??_NQCDN_FiltrApp) --to jest filtr aplikacji
(...)

Niestety nie bardzo rozumiem dlaczego :-/.
Tzn. wiem dlaczego dodaje mi pozycje dla których jest ustawiona druga kategoria, ale nie bardzo rozumiem dlaczego dobrze mi sumuje pozycje bez drugiej kategorii...

1

Kilka uwag:

(...)
FROM
    cdn.vatnag
--dodawaj tabele po których robisz join
INNER JOIN cdn.vattab ON vat_cdn.vattab.vanid = cdn.vatnag.van_vanid
--tu tez brakuje tabeli z lewej strony
INNER JOIN CDN.KATEGORIE A ON VAT_KATID = A.KAT_KATID
--prze left join nie musisz pisać: KAT2ID IS NOT NULL w warunku, left weżmie wszytkie pasujące, a te które nie maja pary zostaną zwrócone
LEFT JOIN CDN.KATEGORIE B ON VAT_ VAT_KAT2ID = B.KAT_KATID
--brak nazw tabel
INNER JOIN CDn.RptZaznaczenia2 ON rpz2_GIDNumer = van_Vanid

WHERE
    (??_NQCDN_FiltrApp) --to jest filtr aplikacji
(...)

Niestety nie bardzo rozumiem dlaczego :-/.
to wynika z złączenia, zapis jaki miałeś:

select * from t1,t2 where t1.id=td2.id

jest równoważny:

select * from t1
inner join t2 on t2.id=t1.id

czyli wybiera tylko pasujące elementy, brak odpowiednika w t2 sprawia, że rekord z t1 nie zostaje zwrócony.
Dlatego trzeba było to załatwić left join

0

@Panczo dziękuję Ci za pomoc. Przyznam, ze ciągle mam masę wątpliwości jak to dokładnie działa, ale spróbuję to sobie jeszcze na spokojnie przeanalizować.

0

Bo starasz sie zrozumieć od razu calość a nie istotę problemu. Musisz zrozumieć na czym polegaja złączenia, więc uprość przykład,

Załóżmy, że masz taką tabelę:

vattab

vanid VAT_KATID VAT_KAT2ID
1 1 1
1 2 1
1 1 2
1 1 (null)
1 2 (null)

i kategorie

KAT_KATID
1
2

Pomiń całą resztę i załóż, że chcesz joinować się po VAT_KAT2ID <-> KAT_KATID, ale zakładasz, że chcesz zwrócić wszystkie rekordy z vattab, ty napisałeś inner join

select
   v.* 
from
    vattab v
    inner join kategorie a on a.kat_katid = v.vat_kat2id

co da w wyniku:

vanid VAT_KATID VAT_KAT2ID
1 1 1
1 2 1
1 1 2

Pominiete zostaną rekordy do których nie ma dopasowania (w tym wypadku null'e, aby zwrócić wszystkie rekordy z lewej strony musisz użyć left join wtedy dostaniesz wszystkie rekordy.

Poćwicz to sobie: http://sqlfiddle.com/#!9/efe180/6

1 użytkowników online, w tym zalogowanych: 0, gości: 1