PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Znajdź nakładające się zakresy dat w PostgreSQL

Aktualnie przyjęta odpowiedź nie odpowiada na pytanie. I w zasadzie jest to złe. a BETWEEN x AND y przekłada się na:

a >= x AND a <= y

W tym górna granica, podczas gdy ludzie zazwyczaj muszą wykluczać to:

a >= x AND a < y

Z datami możesz łatwo dostosować. W przypadku roku 2009 jako górną granicę użyj „2009-12-31”.
Ale z sygnaturami czasowymi to nie jest takie proste. które pozwalają na cyfry ułamkowe. Nowoczesne wersje Postgresa używają wewnętrznie 8-bajtowej liczby całkowitej do przechowywania do 6 ułamków sekund (rozdzielczość µs). Wiedząc o tym, moglibyśmy nadal sprawiają, że działa, ale nie jest to intuicyjne i zależy od szczegółów implementacji. Zły pomysł.

Ponadto a BETWEEN x AND y nie znajduje nakładających się zakresów. Potrzebujemy:

b >= x AND a < y

I gracze, którzy nigdy nie odeszli nie są jeszcze brane pod uwagę.

Właściwa odpowiedź

Zakładając rok 2009 , przeformułuję pytanie, nie zmieniając jego znaczenia:

„Znajdź wszystkich graczy danej drużyny, którzy dołączyli przed 2010 r. i nie opuścili przed 2009 r.”

Zapytanie podstawowe:

SELECT p.*
FROM   team     t
JOIN   contract c USING (name_team) 
JOIN   player   p USING (name_player) 
WHERE  t.name_team = ? 
AND    c.date_join  <  date '2010-01-01'
AND    c.date_leave >= date '2009-01-01';

Ale jest więcej:

Jeśli integralność referencyjna jest wymuszona ograniczeniami FK, tabela team samo w sobie jest tylko szumem w zapytaniu i można je usunąć.

Chociaż ten sam gracz może opuścić i ponownie dołączyć do tej samej drużyny, musimy również złożyć ewentualne duplikaty, na przykład za pomocą DISTINCT .

A my możemy trzeba uwzględnić szczególny przypadek:graczy, którzy nigdy nie odeszli. Zakładając, że ci gracze mają NULL w date_leave .

„Zakłada się, że zawodnik, o którym nie wiadomo, że odszedł, gra w drużynie do dziś”.

Dopracowane zapytanie:

SELECT DISTINCT p.* 
FROM   contract c
JOIN   player   p USING (name_player) 
WHERE  c.name_team = ? 
AND    c.date_join  <  date '2010-01-01'
AND   (c.date_leave >= date '2009-01-01' OR c.date_leave IS NULL);

Pierwszeństwo operatorów działa na naszą niekorzyść, AND wiąże przed OR . Potrzebujemy nawiasów.

Powiązana odpowiedź ze zoptymalizowanym DISTINCT (jeśli duplikaty są częste):

  • Stoły od wielu do wielu — wydajność jest zła

Zazwyczaj nazwy osób fizycznych nie są unikatowe i używany jest zastępczy klucz podstawowy. Ale oczywiście name_player jest głównym kluczem player . Jeśli potrzebujesz tylko nazw graczy, nie potrzebujemy stołu player w zapytaniu:

SELECT DISTINCT name_player 
FROM   contract
WHERE  name_team = ? 
AND    date_join  <  date '2010-01-01'
AND   (date_leave >= date '2009-01-01' OR date_leave IS NULL);

SQL OVERLAPS operator

Instrukcja:

OVERLAPS automatycznie przyjmuje wcześniejszą wartość pary jako początek. Każdy okres jest uważany za reprezentujący półotwarty interwał start <= time < end , chyba że start i end są równe, w którym to przypadku reprezentuje ten pojedynczy moment.

Aby zadbać o potencjalne NULL wartości, COALESCE wydaje się najłatwiejsze:

SELECT DISTINCT name_player 
FROM   contract
WHERE  name_team = ? 
AND    (date_join, COALESCE(date_leave, CURRENT_DATE)) OVERLAPS
       (date '2009-01-01', date '2010-01-01');  -- upper bound excluded

Typ zakresu z obsługą indeksów

W Postgresie 9.2 lub nowszym możesz także operować z rzeczywistymi typami zasięgu :

SELECT DISTINCT name_player 
FROM   contract
WHERE  name_team = ? 
AND    daterange(date_join, date_leave) &&
       daterange '[2009-01-01,2010-01-01)';  -- upper bound excluded

Typy zakresów dodają trochę narzutu i zajmują więcej miejsca. 2 x date =8 bajtów; 1 x daterange =14 bajtów na dysku lub 17 bajtów w pamięci RAM. Ale w połączeniu z operatorem nakładania się && zapytanie może być obsługiwane przez indeks GiST.

Ponadto nie ma potrzeby stosowania specjalnych wielkości liter w wartościach NULL. NULL oznacza „otwarty zakres” w typie zakresu – dokładnie to, czego potrzebujemy. Definicja tabeli nie musi się nawet zmieniać:możemy utworzyć typ zakresu w locie - i wesprzeć zapytanie pasującym indeksem wyrażenia:

CREATE INDEX mv_stock_dr_idx ON mv_stock USING gist (daterange(date_join, date_leave));

Powiązane:

  • Tabela średniej historii akcji


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Kopiowanie bazy danych PostgreSQL na inny serwer

  2. Jak wykonać kopię zapasową i przywrócić bazę danych PostgreSQL

  3. Różnica między językiem sql a językiem plpgsql w funkcjach PostgreSQL

  4. Jak zadeklarować zmienną w PostgreSQL

  5. Wyjątek w JPA podczas używania pliku seed dla PostgreSQL