sql >> Database >  >> RDS >> PostgreSQL

Uren aftrekken van de nu() functie

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?

  1. AT TIME ZONE 'CET' transformeert de timestamp waarde eventtime naar timestamptz 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).

  2. 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 naar timestamptz , 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:BETWEEN is bijna altijd fout met 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


  1. Hoe fouten in sqlplus weer te geven

  2. Hoe een kolom in SQL te laten vallen?

  3. Oracle SQL Analytic-query - recursief spreadsheet-achtig lopend totaal

  4. MySQL's alternatief voor T-SQL's WITH TIES