PostgreSQL - operator except
Być może ktoś jeszcze nie używał nigdy operatora "except" w Postgresie. Jak dotąd też go nie używałem:) Właściwie to nie wiedziałem o jego istnieniu, a z konieczności (i jak się również okazało - z wygody) można czasem z niego skorzystać.
Ostatnio dostałem zlecenie modyfikacji małej aplikacji www. Pomyślałem jak zawsze - prościzna;) Strona miała za zadanie zbierać dane o uczestnikach szkoleń, a dane były przechowywane w jednej tabeli. Działało to na Postgresie. Danych nie było wiele, bo nieco ponad 9 tys. rekordów.
Firma chciała, żeby z tej bazy dało się wyciągnąć jakieś ciekawe informacje - o osobach powracających na kolejne szkolenia, o firmach, które były już kiedyś na jakimś wydarzeniu. Jednym słowem - miało powstać coś na kształt raportów.
Dla ułatwienia przedstawiam maly wycinek tabeli:
-------------------------------------------------------------------------------------- | id | imie | nazwisko | firma | data_szkolenia | data_seminarium | data_konferencji | -------------------------------------------------------------------------------------- | 1 | a | aa | aaa | 2009-11-21 | NULL | NULL | | 2 | b | bb | bbb | NULL | 2009-12-02 | NULL | | 3 | c | cc | ccc | 2010-03-21 | NULL | NULL | | 4 | b | bb | bbb | NULL | NULL | 2010-06-12 | | 5 | e | ee | eee | 2009-07-04 | NULL | NULL | | 6 | b | bb | bbb | NULL | NULL | 2010-01-28 | | 7 | f | ff | fff | NULL | 2010-02-09 | NULL | --------------------------------------------------------------------------------------
Każda wizyta danej osoby była zapisywana w nowym rekordzie. W jaki sposób znaleźć osoby, które były tylko w 2009 lub 2010 roku? Tu z pomocą przychodzi nam wspomniany na początku operator "except".
Najpierw wybieramy wszystkie osoby z bazy.
select distinct(imie || nazwisko || firma) from szkoleniaPóźniej wybieramy tylko te osoby, które były na szkoleniu w 2009 roku (załóżmy, że w basie są tylko szkolenia dla 2009 i 2010 roku - sprawa się nieco komplikuje, jeśli są też poprzednie lata - 2007, 2008):
select distinct(imie || nazwisko || firma) from szkolenia where (data_szkolenia < '2010-01-01' or data_seminarium < '2010-01-01' or data_konferencji < '2010-01-01')Ostatecznie korzystamy z operatora "except", żeby wykluczyć z wszystkich osób te, które były już na szkoleniu w roku 2009:
select distinct(imie || nazwisko || firma) from szkolenia except select distinct(imie || nazwisko || firma) from szkolenia where (data_szkolenia < '2010-01-01' or data_seminarium < '2010-01-01' or data_konferencji < '2010-01-01')I otrzymujemy listę osób, które były tylko w tym roku.
Być może da się to zrobić w inny sposób (a pewnie się da), jednak to wydaje mi się proste, łatwe i przyjemne w użyciu (nie piszę, że optymalne;))