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 
	szkolenia
			
Póź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;))

Tagi: PostgreSQL, bazy danych, except