sql >> Databasteknik >  >> RDS >> PostgreSQL

Subtrahera timmar från funktionen now().

Svar för timestamp

Du måste förstå arten av datatyperna timestamp (timestamp without time zone ) och timestamptz (timestamp with time zone ). Om du inte gör det, läs detta först:

  • Ignorerar tidszoner helt och hållet i Rails och PostgreSQL

AT TIME ZONE konstruktionen transformerar en timestamp till timestamptz , vilket nästan säkert är fel drag för ditt fall:

where eventtime at time zone 'CET' between '2015-06-16 06:00:00'
                                       and '2015-06-17 06:00:00'

Först , det dödar prestandan. Använder AT TIME ZONE till kolumnen eventtime gör uttrycket inte sargbart . Postgres kan inte använda vanliga index på eventtime . Men även utan index är sargbara uttryck billigare. Justera filtervärden istället för att manipulera varje radvärde.
Du kunde kompensera med ett matchande uttrycksindex, men det är förmodligen bara ett missförstånd och fel ändå.

Vad händer i det uttrycket?

  1. AT TIME ZONE 'CET' omvandlar timestamp värde eventtime till timestamptz genom att lägga till tidsförskjutningen för din nuvarande tidszon. När du använder en tidszon namn (inte en numerisk offset eller en förkortning), detta tar även hänsyn till DST-regler (sommartid), så du får en annan offset för "vinter" tidsstämplar. I grund och botten får du svaret på frågan:

    Vad är motsvarande UTC-tidsstämpel för den givna tidsstämpeln i den givna tidszonen?

    När du visar resultatet till användaren formateras som lokal tidsstämpel med motsvarande tidsförskjutning för sessionens aktuella tidszon. (Kan eller kanske inte är samma som den som används i uttrycket).

  2. Strängliteralerna på höger sida har ingen datatyp, så typen härleds från tilldelningen i uttrycket. Eftersom det är timestamptz nu castas båda till timestamptz , antar sessionens aktuella tidszon.

    Vad är motsvarande UTC-tidsstämpel för den givna tidsstämpeln för tidszonsinställningen för den aktuella sessionen.

    Offsetet kan variera med sommarreglerna.

Lång historia kort , om du alltid arbeta med samma tidszon:CET eller 'Europe/Berlin' - Samma sak för dagens tidsstämplar, men inte för historiska eller (eventuellt) framtida, du kan bara klippa av tyget.

Det andra problemet med uttrycket:BETWEEN är nästan alltid fel med timestamp värden. Se:

  • Optimera MELLAN datumuppgift
  • Hitta överlappande datumintervall i 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() är Postgres-implementeringen av SQL-standarden CURRENT_TIMESTAMP . Båda returnerar timestamptz (inte timestamp !). Du kan använda antingen.
now()::date motsvarar CURRENT_DATE . Båda beror på den aktuella tidszonsinställningen.

Du bör ha ett index av formuläret:

CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)

Eller, för att tillåta endast index-skanningar:

CREATE INDEX foo2 ON t_el_eventlog(sourceid, eventtime, serialnumber)

Om du arbetar i olika tidszoner blir saker och ting mer komplicerade och du bör använda timestamptz för allt.

Alternativ för timestamptz

Innan frågeuppdateringen verkade det som om tidszoner spelar roll. När du har att göra med olika tidszoner, "idag" är ett funktionellt beroende av den aktuella tidszonen. Folk tenderar att glömma det.

För att bara arbeta med den aktuella tidszonsinställningen för sessionen, använd samma fråga som ovan. Om de körs i en annan tidszon är resultaten felaktiga i verkligheten. (Gäller även ovanstående.)

För att garantera ett korrekt resultat för en given tidszon ('Europa/Berlin' i ditt fall) oavsett den aktuella tidszonsinställningen för sessionen, använd istället detta uttryck:

    ((now() AT TIME ZONE 'Europe/Berlin')::date - interval '18 hours')
            AT TIME ZONE 'Europe/Berlin'  -- 2nd time to convert back

Tänk på att AT TIME ZONE konstruktion returnerar timestamp för timestamptz input och vice versa.

Som nämndes i början, alla blodiga detaljer här:

  • Ignorerar tidszoner helt och hållet i Rails och PostgreSQL


  1. Få ID:t för en ny post insatt i en databas från den returnerade Uri

  2. Kontrollera din SQLite-version

  3. Splitting Strings:Nu med mindre T-SQL

  4. Köra ett SSIS-paket med dtexec