[FIREBIRD] Problem z grupowaniem

0

Zamierzam stworzyć w oparciu o tablę ZAKUPY listę pogrupowaną wg nr towarów i najlepszej ceny.
Lista ta rownież powinna zawierac ID tej tabeli (będący PRIMARY KEY)

Czyli podstawą wyjścia jest:
select ZK.NR_TOWARU, min(ZK.CENA) from ZAKUPY ZK where ZK.DATA between '2008' and '2007' group by ZK.NR_TOWARU

I teraz, jak dodać do utworzonej listy jeszcze ID z tabeli ZAKUPY. Oczywiscie grupowanie wg ID z wiadomych powodów odpada.

Pewnym rozwiązaniem jest operator IN w klauzuli where

czyli:
select ZK.ID, ZK.NR_TOWARU, min(ZK.CENA) from ZAKUPY ZK where ZK.DATA between '2008' and '2007' and ZK.ID in ( select ZK.NR_TOWARU, min(ZK.CENA) from ZAKUPY ZK where ZK.DATA between '2008' and '2007' group by ZK.NR_TOWARU
)

 
Jednak przy dłuższym okresie  strasznie długo  trwa realizacja zapytania - kilka godz -  i jest to niedposzczalne.

Czy jest inna metoda ?
0

To drugie to akurat powinno szybko zadziałać bo chyba jest nieprawidłowe : ) A poza tym które chcesz otrzymać ID jeśli będą dwie minimalne ceny ?

0

Drugie zapytanie jest na pewno nieprawidłowe, w ogóle nie powinno się skompilować, bo podzapytanie zwraca więcej niż 1 kolumnę.

Nie wiem, czy chodzi Ci o coś takiego:

id, nr_zak, cena
1, 1, 15
2, 1, 12
3, 1, 10
4, 2, 21
5, 2, 26
6, 2, 22

Wynik:
id, nr_zak, cena
3, 1, 10
4, 2, 21

Możesz zrobić to w ten sposób:

SELECT a.id, a.nr_zak, a.cena FROM zakupy a NATURAL JOIN 
  (SELECT nr_zak, min(cena) as cena FROM zakupy [WHERE ...] GROUP BY nr_zak) b;
0

Tak dla uzupełnienia NATURAL JOIN jest od wersji 2.1. Tak czy siak, taka opcja zaaowocuje zdublowaniem wpisów w sytuacji gdy w pierwszej tabeli będą dwa rekordy z taką samą ceną minimalną.

SELECT a.id, a.nr_zak, a.cena FROM zakupy a JOIN 
  (SELECT nr_zak, min(cena) as cena FROM zakupy [WHERE ...] GROUP BY nr_zak) b on a.nr_zak=b.nr_zak and a.cena=b.cena;
0
b0bik napisał(a)

To drugie to akurat powinno szybko zadziałać bo chyba jest nieprawidłowe : ) A poza tym które chcesz otrzymać ID jeśli będą dwie minimalne ceny ?

Uważam, że to jest poprawna składnia, ponieważ operator IN jest równoważny operatorowi OR, czyli:

where (NR_TOWARU=1 or NR_TOWARU=2 or NR_TOWARU=3) == where NR_TOWARU IN (select NR_TOWARU from ZAKUPY where NR_TOWARU<4)
Jeśli wezmę dla kilku dni , to dokładnie wyrzuca to, czego oczekuje - bez żadnych błędów .

Krolik_ dokładnie o taki wynik mi chodzi.

Niestety zaproponowane przez Was rozwiązanie jest niewłaściwe, bo wiąże po cenie. Już to sprawdzałem wczesniej na kilka sposobów. Skutkuje to tym, że :
a.jeśli w całym badanym okresie będzie taka sama cena różnych dostaw , to wyrzuci wszystkie dostawy
b.jesli w całym badanym okresie będzie kilka cen odpowiadajacych min ostatniej dostawy, to wyrzuci te wszystkie dostawy
Nie wiem, czy jest jakies rozwiązanie - myślałem, że może jest jakaś "magiczna "klauzula ;-) , która pozwala dodać pola, które nie powinny podlegać grupowaniu

SELECT ID , NR_TOWARU, MIN(CENA) FROM ZAKUPY
WHERE....
GROUP BY NR_TOWARU

TUTAJ dodaje magiczną klauzulę pozwalająca dodać ID odpowiadający minimalnej cenie

(a dla takiej samej ceny zakupu powtarzającej sie w tym samym dniu mozna dodac klauzulę FIRST )

0

Drogi kolego z całym szacunkiem, ale to co Ty uważasz ma się nijak do tego co napisałeś. Zapytanie z pierwszego Twojego posta jest BŁĘDNE. To co napisałeś powyżej jest prawidłowe.

Poza tym proponuje czytać to co Ci ktoś odpowiada. Jak możesz pisać że zaproponowane rozwiązanie jest nieprawidłowe, skoro w drugim poście zadałem Ci pytanie co według Ciebie jest prawidłowe przy dwóch takich samych cenach.

Eh. Poza tym nie ma rzeczy niemożliwych. Skoro oczekujesz że zostanie Ci tylko jeden towar, to pytanie który ? Jeśli obojętne, to sprawę można zrobić prosto - napisać procedurę składowaną, która z zaproponowanego przez Krolika rozwiązania zwróci tylko krotki o unikalnych NR_TOWARU. Robisz kursor lub pętle po tym zapytaniu, w pomocniczej zmiennej trzymasz ostatnie NR_TOWARU i sprawdzasz czy się zmieniło, jak tak to SUSPEND jak nie to nie.

Pozdrawiam
b

0
b0bik napisał(a)

Drogi kolego z całym szacunkiem, ale to co Ty uważasz ma się nijak do tego co napisałeś. Zapytanie z pierwszego Twojego posta jest BŁĘDNE. To co napisałeś powyżej jest prawidłowe.

Poza tym proponuje czytać to co Ci ktoś odpowiada. Jak możesz pisać że zaproponowane rozwiązanie jest nieprawidłowe, skoro w drugim poście zadałem Ci pytanie co według Ciebie jest prawidłowe przy dwóch takich samych cenach.

Eh. Poza tym nie ma rzeczy niemożliwych. Skoro oczekujesz że zostanie Ci tylko jeden towar, to pytanie który ? Jeśli obojętne, to sprawę można zrobić prosto - napisać procedurę składowaną, która z zaproponowanego przez Krolika rozwiązania zwróci tylko krotki o unikalnych NR_TOWARU. Robisz kursor lub pętle po tym zapytaniu, w pomocniczej zmiennej trzymasz ostatnie NR_TOWARU i sprawdzasz czy się zmieniło, jak tak to SUSPEND jak nie to nie.

Pozdrawiam
b

Dzieki bObik .
Sorry, wrzuciłem nie ten kod do podzapytania :-D .
Miałem nadzieję, że da sie to bez procedury. Zakładałem , że moja wiedza jest na tyle uboga, że nie znam wszystkich możliwości dotyczących grupowania (wczesniej np. nie znałem rozszerzenia z rollup) .

0

Bez jaj, żeby wyeliminować duplikaty nie wystarczy SELECT DISTINCT ON (nr_zak, cena) .... ?
Chyba, że w Firebird to nie działa.

BTW. Zapytanie z IN jest jak najbardziej ok i jakbyś wrzucił je w coś komercyjnego, to wykonałoby się tak samo szybko jak mój join.

0

@Krolik:

NIE w FB nie działa DISTINCT ON.

Tak zapytanie z IN jest jak najbardziej OK i nie trzeba go nawet wrzucać do niczego komercyjnego żeby działało, chodziło mi jednak o to że zapytanie z pierwszego posta było skopane - nie miało prawa się wykonać nawet w bardzo komercyjnych narzędziach (co też potwierdził autor).

Pozdrawiam
b

0

Wiem, tylko autorowi chodziło o to, aby ten IN (niepoprawny, ale domyśliłem się) wykonał się szybko.
Dobre systemy przekształcają takie podzapytania z IN na JOINa wewnętrznie, uważając przy tym na duplikaty. Nie znam FB, ale na pewno MySQL i PostgreSQL tego nie potrafi zrobić z podzapytaniami skorelowanymi. Wykona, ale naiwnie, poprzez wykonanie podzapytania w IN dla każdego rekordu z zewnętrznego zapytania. Oracle za to bez problemu to na joina przeksztalci.
Różnica w wydajności dla dużych tabel jest ogromna, dlatego już nawet lepiej tę procedurę składowaną napisać dla usunięcia duplikatów, niż pozwolić bazie naiwnie wykonywać to zapytanie z IN.

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