Zapytanie Postgresql między zakresami dat


126

Próbuję wysłać zapytanie do mojej bazy danych postgresql, aby zwrócić wyniki, w których data przypada w określonym miesiącu i roku. Innymi słowy, chciałbym podać wszystkie wartości z miesiąca na rok.

Jak dotąd mogłem to zrobić tylko w ten sposób:

SELECT user_id 
FROM user_logs 
WHERE login_date BETWEEN '2014-02-01' AND '2014-02-28'

Problem polega na tym, że przed wykonaniem zapytania w tabeli muszę obliczyć pierwszą i ostatnią datę. Czy jest na to prostszy sposób?

Dzięki


Czy przerwa jest zawsze miesięczna, czy może być tak, że zaczynasz być może o 15, a kończysz 7 dnia następnego miesiąca?
frlan

Odpowiedzi:


202

Dzięki datom (i godzinom) wiele rzeczy staje się prostszych, jeśli używasz >= start AND < end.

Na przykład:

SELECT
  user_id
FROM
  user_logs
WHERE
      login_date >= '2014-02-01'
  AND login_date <  '2014-03-01'

W takim przypadku nadal musisz obliczyć datę rozpoczęcia miesiąca, którego potrzebujesz, ale powinno to być proste na wiele sposobów.

Data zakończenia jest również uproszczona; po prostu dodaj dokładnie jeden miesiąc. Bez majstrowania przy 28, 30, 31 itd.


Ta struktura ma również tę zaletę, że jest w stanie utrzymać użycie indeksów.


Wiele osób może zasugerować taki formularz, jak poniższy, ale nie używają indeksów:

WHERE
      DATEPART('year',  login_date) = 2014
  AND DATEPART('month', login_date) = 2

Obejmuje to obliczenie warunków dla każdego wiersza w tabeli (skanowanie) i nie używanie indeksu do znalezienia zakresu wierszy, które będą pasować (wyszukiwanie zakresu).


1
Pierwsze podejście wymaga mieszania miesięcy (zamiast dni), np. 2014-12-01& 2015-01-01. Drugi też może być indeksowany, ale przyznaję, że nie jest to trywialne - EXTRACT()wydaje się bardziej kompatybilne.
pozs

1
Możesz uniknąć obliczania dat w swojej aplikacji, używając interwału. np .: WHERE login_date> = '2014-02-01' AND login_date <'2014-02-01' :: date + INTERVAL '1 miesiąc' Nadal używa indeksów, jednocześnie upraszczając kod.
baswell

53

Od wersji 9.2 PostreSQL obsługiwane są typy zakresów . Możesz więc napisać to tak:

SELECT user_id
FROM user_logs
WHERE '[2014-02-01, 2014-03-01]'::daterange @> login_date

powinno to być bardziej wydajne niż porównanie ciągów


23

Na wypadek gdyby ktoś tu wylądował ... od 8.1 możesz po prostu użyć:

SELECT user_id 
FROM user_logs 
WHERE login_date BETWEEN SYMMETRIC '2014-02-01' AND '2014-02-28'

Z dokumentów:

BETWEEN SYMMETRIC to to samo, co BETWEEN, z wyjątkiem tego, że nie ma wymogu, aby argument po lewej stronie AND był mniejszy lub równy argumentowi po prawej stronie. Jeśli tak nie jest, te dwa argumenty są automatycznie zamieniane, więc zawsze implikowany jest niepusty zakres.


-1
SELECT user_id 
FROM user_logs 
WHERE login_date BETWEEN '2014-02-01' AND '2014-03-01'

Słowo kluczowe Between działa wyjątkowo w przypadku daty. zakłada, że ​​dla dat jest godzina 00:00:00 (tj. północ).


Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.