Połączenie konkretnych produktów po peselu

0

Cześć, mam problem z wyznaczeniem case, chyba, że akurat to nie jest problemem.

Mam tabelę o nazwach Pesel, data_transakcji, nazwa_produktu.
W nazwa_produktu znajdują się nie tylko produkty : telefon, ładowarka i słuchawki, ale też wiele innych.
Muszę wyznaczyć case, który będzie wyznaczał zestawy ( czyli np. klient kupił tylko telefon (zestaw1)):
Zestaw1 : Telefon
Zestaw2 : Telefon + ładowarka
Zestaw3 : Telefon + słuchawki
Zestaw4 : Telefon + słuchawki + ładowarka

SELECT
A.Pesel,
(case 
when A.Pesel = B.Pesel AND C.nazwa_produktu IS NULL then zestaw2
when A.Pesel = C.Pesel AND B.nazwa_produktu IS NULL then zestaw3
when A.Pesel = B.Pesel AND  B.Pesel = C.Pesel   then zestaw4
when nazwa produktu = 'Telefon' then zestaw1 
else 0 end) as Zestawy

FROM
(SELECT
PESEL as Pesel
FROM tabela 
WHERE data_transakcji >= '2023-01-01' AND 
nazwa_produktu = 'Telefon') as A

INNER JOIN 
(SELECT
 PESEL as Pesel
FROM tabela 
WHERE data_transakcji >= '2023-01-01' AND 
nazwa_produktu  = 'Ładowarka') as B

INNER JOIN 
(SELECT
PESEL as Pesel
FROM tabela 
WHERE data_transakcji >= '2023-01-01' AND 
nazwa_produktu  = 'Słuchawki') as C

ON A.Pesel = B.Pesel
ON B.Pesel = C.Pesel
;

Problem jest taki, że wszystko się miesza i ten case nie działa, daje niepoprawne wyniki.
To mój jedyny pomysł na połączenie tego PESELU,
czy jedyny problemem jest tutaj case, czy coś więcej?
Z Góry dziękuję za pomoc.

0

Po pierwsze, skoro ten kod nie sypie Ci błędami od samego początku, to znaczy, że uruchamiasz go w mysql/mariadb :)

Po drugie, jeśli chcesz pisać coś dłuższego, niż jedna linijka, to zacznij to sobie formatować (wcięcia), bo inaczej nie znajdziesz błędów. A jeśli już formatujesz, to wklejając kod na forum otulaj go odpowiednim formatowaniem, żebyśmy i my to widzieli.

Po trzecie, zachęcam (inni mogą się nie zgodzić), by pisać kod w miarę uniwersalnie, bez tych mysqlowych wodotrysków - ułatwi i to analizę:

  • klauzulę ON umieszczaj po złączeniu, którego dotyczy, zamiast na końcu zapytania
  • w głównym zapytaniu (czyli tam, gdzie masz tego CASE'a nie używaj kolumn, których nie ma w podzapytaniu (np. C.nazwa_produktu).

Po zastosowaniu powyższych założeń otrzymałbyś taki kod:

SELECT
A.Pesel,
case
	when B.Pesel IS NOT NULL AND C.Pesel IS NULL then 'zestaw2'
	when C.Pesel IS NOT NULL AND B.Pesel IS NULL then 'zestaw3'
	when B.Pesel IS NOT NULL AND C.Pesel IS NOT NULL then 'zestaw4'
	else 'zestaw1' 
	end as Zestawy

FROM
(
	SELECT PESEL as Pesel
	FROM tabela
	WHERE data_transakcji >= '2023-01-01' 
		AND nazwa_produktu = 'Telefon'
) as A
INNER JOIN (
	SELECT PESEL as Pesel
	FROM tabela
	WHERE data_transakcji >= '2023-01-01' 
		AND nazwa_produktu = 'Ładowarka'
) as B ON A.Pesel = B.Pesel
INNER JOIN (
	SELECT PESEL as Pesel
	FROM tabela
	WHERE data_transakcji >= '2023-01-01' 
		AND nazwa_produktu = 'Słuchawki'
) as C ON B.Pesel = C.Pesel
;

Teraz zauważ, że wybierasz wszystkie sprzedaże telefonów, ładowarek i słuchawek i wykonujesz iloczyn tych zbiorów (czyli na dzień dobry filtrujesz sobie jedynie tych klientów, którzy kupili wszystkie trzy przedmioty, więc CASE nie jest Ci potrzebny, bo i tak znajdziesz jedynie zestaw3

Żeby znaleźć też inne zestawy, musisz zamienić INNER JOIN na LEFT JOIN.

Swoją drogą, czy jeśli w styczniu kupię telefon, a w maju słuchawki, to chciałbyś to widzieć jako zestaw 3?
No i kolejna sprawa - jeśli kupię telefon z ładowarką i słuchawkami (zestaw 4), a do tego dokupię słuchawki, to Twoje zapytanie da Ci informację o tym, że kupiłem dwa zestawy 4. Tego oczekujesz? Jeśli nie, to musisz pomyśleć o jakimś DISTINCT gdzieś po drodze.

Jeśli masz tych zestawów kilka, to możesz sobie tak dłubać.
Ale jeśli masz kilkanaście/kilkadziesiąt/nie-daj-Boże-kilkaset produktów i z nich tworzysz znowu dziesiątki zestawów, to na pewno nie tędy droga.
Na szybko przychodzą mi do głowy dwa (pewnie głupie, ale pojawią się koledzy, którzy mnie za to zrugają) pomysły:

  • Przy max 64 produktach można byłoby kodować je binarnie (telefon to 1, ładowarka 2, słuchawki 4, etui 8, itd), sumować i tym sumom przypisać zestawy (7 to zestaw 3)
  • Przy większej liczbie produktów można łączyć nazwy (choć lepiej byłoby to robić na jakichś IDkach) produktów kupionych przez jednego człowieka w jeden string rozdzielając poszczególne nazwy np. średnikami i porównywać ze słownikiem.
0
Fac napisał(a):

Po pierwsze, skoro ten kod nie sypie Ci błędami od samego początku, to znaczy, że uruchamiasz go w mysql/mariadb :)

Po drugie, jeśli chcesz pisać coś dłuższego, niż jedna linijka, to zacznij to sobie formatować (wcięcia), bo inaczej nie znajdziesz błędów. A jeśli już formatujesz, to wklejając kod na forum otulaj go odpowiednim formatowaniem, żebyśmy i my to widzieli.

Po trzecie, zachęcam (inni mogą się nie zgodzić), by pisać kod w miarę uniwersalnie, bez tych mysqlowych wodotrysków - ułatwi i to analizę:

  • klauzulę ON umieszczaj po złączeniu, którego dotyczy, zamiast na końcu zapytania
  • w głównym zapytaniu (czyli tam, gdzie masz tego CASE'a nie używaj kolumn, których nie ma w podzapytaniu (np. C.nazwa_produktu).

Po zastosowaniu powyższych założeń otrzymałbyś taki kod:

SELECT
A.Pesel,
case
	when B.Pesel IS NOT NULL AND C.Pesel IS NULL then 'zestaw2'
	when C.Pesel IS NOT NULL AND B.Pesel IS NULL then 'zestaw3'
	when B.Pesel IS NOT NULL AND C.Pesel IS NOT NULL then 'zestaw4'
	else 'zestaw1' 
	end as Zestawy

FROM
(
	SELECT PESEL as Pesel
	FROM tabela
	WHERE data_transakcji >= '2023-01-01' 
		AND nazwa_produktu = 'Telefon'
) as A
INNER JOIN (
	SELECT PESEL as Pesel
	FROM tabela
	WHERE data_transakcji >= '2023-01-01' 
		AND nazwa_produktu = 'Ładowarka'
) as B ON A.Pesel = B.Pesel
INNER JOIN (
	SELECT PESEL as Pesel
	FROM tabela
	WHERE data_transakcji >= '2023-01-01' 
		AND nazwa_produktu = 'Słuchawki'
) as C ON B.Pesel = C.Pesel
;

Teraz zauważ, że wybierasz wszystkie sprzedaże telefonów, ładowarek i słuchawek i wykonujesz iloczyn tych zbiorów (czyli na dzień dobry filtrujesz sobie jedynie tych klientów, którzy kupili wszystkie trzy przedmioty, więc CASE nie jest Ci potrzebny, bo i tak znajdziesz jedynie zestaw3

Żeby znaleźć też inne zestawy, musisz zamienić INNER JOIN na LEFT JOIN.

Swoją drogą, czy jeśli w styczniu kupię telefon, a w maju słuchawki, to chciałbyś to widzieć jako zestaw 3?
No i kolejna sprawa - jeśli kupię telefon z ładowarką i słuchawkami (zestaw 4), a do tego dokupię słuchawki, to Twoje zapytanie da Ci informację o tym, że kupiłem dwa zestawy 4. Tego oczekujesz? Jeśli nie, to musisz pomyśleć o jakimś DISTINCT gdzieś po drodze.

Jeśli masz tych zestawów kilka, to możesz sobie tak dłubać.
Ale jeśli masz kilkanaście/kilkadziesiąt/nie-daj-Boże-kilkaset produktów i z nich tworzysz znowu dziesiątki zestawów, to na pewno nie tędy droga.
Na szybko przychodzą mi do głowy dwa (pewnie głupie, ale pojawią się koledzy, którzy mnie za to zrugają) pomysły:

  • Przy max 64 produktach można byłoby kodować je binarnie (telefon to 1, ładowarka 2, słuchawki 4, etui 8, itd), sumować i tym sumom przypisać zestawy (7 to zestaw 3)
  • Przy większej liczbie produktów można łączyć nazwy (choć lepiej byłoby to robić na jakichś IDkach) produktów kupionych przez jednego człowieka w jeden string rozdzielając poszczególne nazwy np. średnikami i porównywać ze słownikiem.

Dzięki za pomoc, to co napisałeś nie zadziałało w 100 %, ale ukierunkowało mnie.
Data transakcji nie ma znaczenia w tym zapytaniu. Przy tak napisanym case'ie właśnie nawet jeśli klient kupi telefon w styczniu, a słuchawki w maju, to będzie wyznaczało zestaw 3, o to chodziło. Do wyznaczenia są rzeczywiście 4 zestawy, ale dzięki za rozbudowaną odpowiedź, może się przyda w przyszłości.
Głównym problemem w moim zapytaniu, było użycie INNER JOIN zamiast LEFT JOIN.
Co do case'a to należało jeszcze określić zestaw 1, bo wrzucało w nim inne produkty.
Kod, który zadziałał wstawiam poniżej, może ktoś również kiedyś będzie miał podobny problem.

SELECT
A.Pesel,
case
when B.Pesel IS NOT NULL AND C.Pesel IS NULL then 'zestaw2'
when C.Pesel IS NOT NULL AND B.Pesel IS NULL then 'zestaw3'
when B.Pesel IS NOT NULL AND C.Pesel IS NOT NULL then 'zestaw4'
when IS NULL AND C.Pesel IS NULL then 'zestaw1'
else '0'
end as Zestawy

FROM
(
SELECT PESEL as Pesel
FROM tabela
WHERE data_transakcji >= '2023-01-01'
AND nazwa_produktu = 'Telefon'
) as A
LEFT JOIN (
SELECT PESEL as Pesel
FROM tabela
WHERE data_transakcji >= '2023-01-01'
AND nazwa_produktu = 'Ładowarka'
) as B ON A.Pesel = B.Pesel
LEFT JOIN (
SELECT PESEL as Pesel
FROM tabela
WHERE data_transakcji >= '2023-01-01'
AND nazwa_produktu = 'Słuchawki'
) as C ON A.Pesel = C.Pesel
;

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