Jeśli faktycznie masz kolumnę typu timestamp
i zinterpretuj go (częściowo) w zależności od aktualnej strefy czasowej, a ta strefa czasowa może się różnić, wtedy indeks jest generalnie niemożliwy . Indeks można zbudować tylko na IMMUTABLE
dane ...
Po aktualizacji:
Aby odpowiedzieć na te pytania:
- Jakie rezerwacje zaczynają się „dzisiaj”?
- Jakie rezerwacje mają daty rozpoczęcia w przyszłości?
- Jakie rezerwacje mają daty rozpoczęcia w przeszłości?
... najlepiej przechowywać timestamp with time zone
. Tylko date
nie jest wystarczająco precyzyjny.
O ile interesuje nas tylko lokalne „dzisiaj” (zdefiniowane przez obecną strefę czasową ), nie trzeba wyraźnie zapisać strefę czasową. Nie obchodzi nas, gdzie na świecie to się dzieje, potrzebujemy tylko absolutnego czasu do porównania.
Następnie, aby uzyskać rezerwacje zaczynające się „dzisiaj”, po prostu:
SELECT *
FROM reservations
WHERE start_on::date = current_date;
Ale to jest nie sargable
ponieważ start_on::date
jest wyrażeniem pochodnym i nie możemy również zbudować dla niego indeksu funkcjonalnego (bez brudnych sztuczek), ponieważ wyrażenie zależy od bieżącej strefy czasowej i nie jest IMMUTABLE
.
Zamiast tego , porównaj z początkiem i końcem „naszego” dnia w czasie UTC:
SELECT *
FROM reservations
WHERE start_on >= current_date::timestamptz
AND start_on < (current_date + 1)::timestamptz; -- exclude upper border
Teraz ten prosty indeks może obsługiwać zapytanie:
CREATE INDEX ON reservations (start_on);
Demo
SQL Fiddle nie działa ATM. Oto mała demonstracja, która pomoże zrozumieć:
CREATE TEMP TABLE reservations (
reservation_id serial
, start_on timestamptz NOT NULL
, time_zone text); -- we don't need this
INSERT INTO reservations (start_on, time_zone) VALUES
('2014-04-09 01:00+02', 'Europe/Vienna')
, ('2014-04-09 23:00+02', 'Europe/Vienna')
, ('2014-04-09 01:00+00', 'UTC') -- the value is independent of the time zone
, ('2014-04-09 23:00+00', 'UTC') -- only display depends on current time zone
, ('2014-04-09 01:00-07', 'America/Los_Angeles')
, ('2014-04-09 23:00-07', 'America/Los_Angeles');
SELECT start_on, time_zone
, start_on::timestamp AS local_ts
, start_on AT TIME ZONE time_zone AS ts_at_tz
, current_date::timestamptz AS lower_bound
, (current_date + 1)::timestamptz AS upper_bound
FROM reservations
WHERE start_on >= current_date::timestamptz
AND start_on < (current_date + 1)::timestamptz;
Więcej wyjaśnień i linków tutaj:
Ignorowanie stref czasowych w Railsach i PostgreSQL