Antwoord voor timestamp
U moet de aard van de gegevenstypen begrijpen timestamp
(timestamp without time zone
) en timestamptz
(timestamp with time zone
). Als je dat niet doet, lees dan eerst dit:
- Tijdzones volledig negeren in Rails en PostgreSQL
De AT TIME ZONE
construct transformeert een timestamp
naar timestamptz
, wat vrijwel zeker de verkeerde zet is voor jouw geval:
where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
and '2015-06-17 06:00:00'
Eerste , het doodt de prestaties. AT TIME ZONE
toepassen naar de kolom eventtime
maakt de uitdrukking niet sargable . Postgres kan geen gewone indexen gebruiken op eventtime
. Maar zelfs zonder index zijn sargable uitdrukkingen goedkoper. Pas filterwaarden aan in plaats van elke rijwaarde te manipuleren.
U zou compenseer met een overeenkomende expressie-index, maar het is waarschijnlijk gewoon een misverstand en hoe dan ook verkeerd.
Wat gebeurt er in die uitdrukking?
-
AT TIME ZONE 'CET'
transformeert detimestamp
waardeeventtime
naartimestamptz
door de tijdverschuiving van uw huidige tijdzone toe te voegen. Bij gebruik van een tijdzone naam (geen numerieke offset of een afkorting), dit houdt ook rekening met DST-regels (zomertijd), dus je krijgt een andere offset voor "winter" tijdstempels. In principe krijg je het antwoord op de vraag:Wat is de corresponderende UTC-tijdstempel voor de opgegeven tijdstempel in de opgegeven tijdzone?
Bij weergeven het resultaat voor de gebruiker wordt geformatteerd als een lokale tijdstempel met de overeenkomstige tijdverschuiving voor de huidige tijdzone van de sessie. (Kan wel of niet dezelfde zijn als die in de uitdrukking wordt gebruikt).
-
De letterlijke tekenreeksen aan de rechterkant hebben geen gegevenstype, dus het type is afgeleid van de toewijzing in de uitdrukking. Want dat is
timestamptz
nu zijn beide gecast naartimestamptz
, uitgaande van de huidige tijdzone van de sessie.Wat is de corresponderende UTC-tijdstempel voor de opgegeven tijdstempel voor de tijdzone-instelling van de huidige sessie.
De offset kan variëren afhankelijk van de zomertijdregels.
Lang verhaal kort , als je altijd werken met dezelfde tijdzone:CET
of 'Europe/Berlin'
- hetzelfde voor huidige tijdstempels, maar niet voor historische of (mogelijk) toekomstige, je kunt gewoon de cruft doorbreken.
Het tweede probleem met de uitdrukking: is bijna altijd fout met BETWEEN
timestamp
waarden. Zie:
- Optimaliseer TUSSEN datumoverzicht
- Vind overlappende perioden in PostgreSQL
SELECT date_trunc('hour', eventtime) AS hour
, count(DISTINCT serialnumber) AS ct -- sure you need distinct?
FROM t_el_eventlog
WHERE eventtime >= now()::date - interval '18 hours'
AND eventtime < now()::date + interval '6 hours'
AND sourceid = 44 -- don't quote the numeric literal
GROUP BY 1
ORDER BY 1;
now()
is de Postgres-implementatie van de SQL-standaard CURRENT_TIMESTAMP
. Beide retourneren timestamptz
(niet timestamp
!). Je kunt beide gebruiken.now()::date
is gelijk aan CURRENT_DATE
. Beide zijn afhankelijk van de huidige tijdzone-instelling.
U zou een index . moeten hebben van het formulier:
CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)
Of om alleen-index scans toe te staan:
CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)
Als je in verschillende tijdzones werkt, wordt het ingewikkelder en moet je timestamptz
. gebruiken voor alles.
Alternatief voor timestamptz
Vóór de update van de vraag leek het alsof tijdzones ertoe doen. Als u te maken heeft met verschillende tijdzones, "vandaag" is een functionele afhankelijkheid van de huidige tijdzone. Mensen hebben de neiging dat te vergeten.
Gebruik dezelfde query als hierboven om alleen met de huidige tijdzone-instelling van de sessie te werken. Indien uitgevoerd in een andere tijdzone, zijn de resultaten in werkelijkheid onjuist. (Geldt ook voor het bovenstaande.)
Om een correct resultaat te garanderen voor een bepaalde tijdzone ('Europa/Berlijn' in uw geval), ongeacht de huidige tijdzone-instelling van de sessie, gebruikt u in plaats daarvan deze uitdrukking:
((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
AT TIME ZONE 'Europe/Berlin' -- 2nd time to convert back
Houd er rekening mee dat de AT TIME ZONE
constructie retourneert timestamp
voor timestamptz
invoer en vice versa.
Zoals aan het begin vermeld, alle bloederige details hier:
- Tijdzones volledig negeren in Rails en PostgreSQL