PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Kurangi jam dari fungsi now()

Jawaban untuk timestamp

Anda perlu memahami sifat tipe data timestamp (timestamp without time zone ) dan timestamptz (timestamp with time zone ). Jika tidak, baca ini dulu:

  • Mengabaikan zona waktu sama sekali di Rails dan PostgreSQL

AT TIME ZONE konstruksi mengubah timestamp ke timestamptz , yang hampir pasti merupakan langkah yang salah untuk kasus Anda:

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

Pertama , itu membunuh kinerja. Menerapkan AT TIME ZONE ke kolom eventtime membuat ekspresi tidak sargable . Postgres tidak dapat menggunakan indeks biasa pada eventtime . Tetapi bahkan tanpa indeks, ekspresi sargable lebih murah. Sesuaikan nilai filter daripada memanipulasi setiap nilai baris.
Anda bisa kompensasi dengan indeks ekspresi yang cocok, tapi itu mungkin hanya kesalahpahaman dan salah.

Apa yang terjadi dalam ekspresi itu?

  1. AT TIME ZONE 'CET' mengubah timestamp nilai eventtime ke timestamptz dengan menambahkan offset waktu dari zona waktu Anda saat ini. Saat menggunakan nama zona waktu (bukan offset numerik atau singkatan), ini juga memperhitungkan aturan DST (waktu musim panas), jadi Anda mendapatkan offset berbeda untuk cap waktu "musim dingin". Pada dasarnya Anda mendapatkan jawaban atas pertanyaan:

    Apa yang dimaksud dengan stempel waktu UTC yang sesuai untuk stempel waktu tertentu di zona waktu tertentu?

    Saat menampilkan hasilnya kepada pengguna diformat sebagai stempel waktu lokal dengan offset waktu yang sesuai untuk zona waktu sesi saat ini. (Mungkin atau mungkin tidak sama dengan yang digunakan dalam ekspresi).

  2. Literal string di sisi kanan tidak memiliki tipe data, jadi tipenya diturunkan dari penugasan dalam ekspresi. Karena itu timestamptz sekarang, keduanya dilemparkan ke timestamptz , dengan asumsi zona waktu sesi saat ini.

    Berapa stempel waktu UTC yang sesuai untuk stempel waktu yang diberikan untuk setelan zona waktu sesi saat ini.

    Offset dapat bervariasi dengan aturan DST.

Singkat cerita , jika Anda selalu beroperasi dengan zona waktu yang sama:CET atau 'Europe/Berlin' - hal yang sama untuk stempel waktu saat ini, tetapi tidak untuk stempel waktu bersejarah atau (mungkin) di masa mendatang, Anda bisa memotongnya.

Masalah kedua dengan ekspresi:BETWEEN hampir selalu salah dengan timestamp nilai-nilai. Lihat:

  • Optimalkan pernyataan ANTARA tanggal
  • Temukan rentang tanggal yang tumpang tindih di 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() adalah implementasi Postgres dari standar SQL CURRENT_TIMESTAMP . Keduanya mengembalikan timestamptz (bukan timestamp !). Anda dapat menggunakan keduanya.
now()::date setara dengan CURRENT_DATE . Keduanya bergantung pada pengaturan zona waktu saat ini.

Anda harus memiliki indeks dalam bentuk:

CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)

Atau, untuk mengizinkan pemindaian hanya indeks:

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

Jika Anda beroperasi di zona waktu yang berbeda, segalanya menjadi lebih rumit dan Anda harus menggunakan timestamptz untuk semuanya.

Alternatif untuk timestamptz

Sebelum pembaruan pertanyaan, sepertinya zona waktu penting. Saat berhadapan dengan zona waktu yang berbeda, "hari ini" adalah ketergantungan fungsional dari zona waktu saat ini. Orang cenderung melupakan itu.

Untuk hanya bekerja dengan pengaturan zona waktu sesi saat ini, gunakan kueri yang sama seperti di atas. Jika dieksekusi di zona waktu yang berbeda, hasilnya sebenarnya salah. (Berlaku untuk hal di atas juga.)

Untuk menjamin hasil yang benar untuk zona waktu tertentu ('Eropa/Berlin' dalam kasus Anda) terlepas dari pengaturan zona waktu sesi saat ini, gunakan ekspresi ini sebagai gantinya:

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

Ketahuilah bahwa AT TIME ZONE konstruksi mengembalikan timestamp untuk timestamptz masukan dan sebaliknya.

Seperti yang disebutkan di awal, semua detail mengerikan di sini:

  • Mengabaikan zona waktu sama sekali di Rails dan PostgreSQL


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Membangun Database yang Sangat Tersedia untuk Moodle Menggunakan PostgreSQL

  2. Ketik string ke integer

  3. Fungsi Buat PostgreSQL

  4. String literal dan karakter escape di postgresql

  5. PostgreSQL tersemat untuk pengujian Java JUnit