PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Sottrarre le ore dalla funzione now()

Risposta per timestamp

È necessario comprendere la natura dei tipi di dati timestamp (timestamp without time zone ) e timestamptz (timestamp with time zone ). Se non lo fai, leggi prima questo:

  • Ignora del tutto i fusi orari in Rails e PostgreSQL

Il AT TIME ZONE costrutto trasforma un timestamp a timestamptz , che è quasi sicuramente la mossa sbagliata per il tuo caso:

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

Prima , uccide le prestazioni. Applicazione di AT TIME ZONE alla colonna eventtime rende l'espressione non sargable . Postgres non può utilizzare indici semplici su eventtime . Ma anche senza indice, le espressioni sargable sono più economiche. Regola i valori del filtro invece di manipolare ogni valore di riga.
Puoi potere compensare con un indice di espressione corrispondente, ma probabilmente è solo un malinteso e comunque sbagliato.

Cosa succede in quell'espressione?

  1. AT TIME ZONE 'CET' trasforma il timestamp valore eventtime a timestamptz aggiungendo la differenza di orario del fuso orario corrente. Quando si utilizza un nome di un fuso orario (non un offset numerico o un'abbreviazione), questo tiene conto anche delle regole dell'ora legale (ora legale), quindi ottieni un offset diverso per i timestamp "invernali". Fondamentalmente ottieni la risposta alla domanda:

    Qual ​​è il timestamp UTC corrispondente per il timestamp specificato nel fuso orario specificato?

    Durante la visualizzazione il risultato per l'utente viene formattato come timestamp locale con l'offset di tempo corrispondente al fuso orario corrente della sessione. (Può essere o meno lo stesso di quello utilizzato nell'espressione).

  2. I valori letterali stringa sul lato destro non hanno alcun tipo di dati, quindi il tipo viene derivato dall'assegnazione nell'espressione. Dato che è timestamptz ora, entrambi vengono trasmessi a timestamptz , assumendo il fuso orario corrente della sessione.

    Qual ​​è il timestamp UTC corrispondente per il timestamp specificato per l'impostazione del fuso orario della sessione corrente.

    L'offset può variare con le regole dell'ora legale.

Per farla breve , se sempre operare con lo stesso fuso orario:CET o 'Europe/Berlin' - stessa cosa per i timestamp attuali, ma non per quelli storici o (possibilmente) futuri, puoi semplicemente tagliare il cruft.

Il secondo problema con l'espressione:BETWEEN è quasi sempre sbagliato con timestamp valori. Vedi:

  • Ottimizza BETWEEN data statement
  • Trova intervalli di date sovrapposti 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() è l'implementazione Postgres dello standard SQL CURRENT_TIMESTAMP . Entrambi restituiscono timestamptz (non timestamp !). Puoi usare entrambi.
now()::date è equivalente a CURRENT_DATE . Entrambi dipendono dall'impostazione del fuso orario corrente.

Dovresti avere un indice del modulo:

CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)

Oppure, per consentire le scansioni solo indice:

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

Se operi in fusi orari diversi, le cose si complicano e dovresti usare timestamptz per tutto.

Alternativa per timestamptz

Prima dell'aggiornamento della domanda, sembrava che i fusi orari fossero importanti. Quando hai a che fare con fusi orari diversi, "oggi" è una dipendenza funzionale del fuso orario corrente. Le persone tendono a dimenticarlo.

Per lavorare solo con l'impostazione del fuso orario corrente della sessione, utilizzare la stessa query di cui sopra. Se eseguito in un fuso orario diverso, i risultati sono in realtà errati. (Vale anche per quanto sopra.)

Per garantire un risultato corretto per un determinato fuso orario ("Europa/Berlino" nel tuo caso) indipendentemente dall'impostazione del fuso orario corrente della sessione, utilizza invece questa espressione:

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

Tieni presente che il AT TIME ZONE costrutto restituisce timestamp per timestamptz input e viceversa.

Come accennato all'inizio, tutti i dettagli cruenti qui:

  • Ignora del tutto i fusi orari in Rails e PostgreSQL