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

Mewakili Tanggal, Waktu, dan Interval di PostgreSQL

PostgreSQL hadir dengan sekumpulan tipe data terkait tanggal dan waktu bawaan. Mengapa Anda harus menggunakannya di atas string atau bilangan bulat? Apa yang harus Anda perhatikan saat menggunakannya? Baca untuk mempelajari lebih lanjut tentang cara bekerja secara efektif dengan tipe data ini di Postgres.

Banyak Jenis

Standar SQL, standar ISO 8601, katalog bawaan PostgreSQL, dan kompatibilitas mundur bersama-sama mendefinisikan sejumlah besar tipe data dan konvensi terkait tanggal/waktu yang tumpang tindih dan dapat disesuaikan yang paling membingungkan. Kebingungan ini biasanya meluas ke kode driver database, kode aplikasi, rutinitas SQL dan menghasilkan bug halus yang sulit untuk di-debug.

Di sisi lain, menggunakan tipe bawaan bawaan menyederhanakan pernyataan SQL dan membuatnya lebih mudah untuk dibaca dan ditulis, dan akibatnya, kurang rawan kesalahan. Menggunakan, katakanlah bilangan bulat (jumlah detik sejak zaman) untuk mewakili waktu, menghasilkan ekspresi SQL yang berat dan kode aplikasi lainnya.

Manfaat dari tipe asli membuatnya bermanfaat untuk mendefinisikan seperangkat aturan yang tidak terlalu menyakitkan dan menerapkannya di seluruh aplikasi dan basis kode operasi. Berikut ini adalah salah satu set tersebut, yang seharusnya memberikan default yang waras dan titik awal yang resonable untuk penyesuaian lebih lanjut jika diperlukan.

Jenis

Gunakan hanya 3 jenis berikut (walaupun banyak tersedia):

  • tanggal - tanggal tertentu, tanpa waktu
  • stempel waktu - tanggal dan waktu tertentu dengan resolusi mikrodetik
  • interval - interval waktu dengan resolusi mikrodetik

Ketiga jenis ini bersama-sama harus mendukung sebagian besar kasus penggunaan aplikasi. Jika Anda tidak memiliki kebutuhan khusus (seperti menghemat penyimpanan), sangat disarankan untuk tetap menggunakan jenis ini saja.

Tanggal mewakili tanggal tanpa waktu, dan cukup berguna dalam praktiknya (lihat contoh di bawah). Jenis stempel waktu adalah varian yang menyertakan informasi zona waktu – tanpa informasi zona waktu, ada terlalu banyak variabel yang dapat memengaruhi interpretasi dan ekstraksi nilai. Terakhir, interval mewakili interval waktu dari serendah mikrodetik hingga jutaan tahun.

String Literal

Gunakan hanya representasi literal berikut, dan gunakan operator pemeran untuk mengurangi verbositas tanpa mengorbankan keterbacaan:

  • '2012-12-25'::date - ISO 8601
  • '2012-12-25 13:04:05.123-08:00'::timestamptz - ISO 8601
  • '1 month 3 days'::interval - Format tradisional Postgres untuk input interval

Menghilangkan zona waktu membuat Anda bergantung pada pengaturan zona waktu server Postgres, konfigurasi TimeZone yang dapat diatur pada tingkat basis data, tingkat sesi, tingkat peran atau dalam string koneksi, pengaturan zona waktu mesin klien, dan lebih banyak faktor seperti itu.

Saat menanyakan dari kode aplikasi, konversikan tipe interval ke unit yang sesuai (seperti hari atau detik) menggunakan extract fungsi dan membaca nilainya sebagai bilangan bulat atau nilai nyata.

Konfigurasi Dan Pengaturan Lainnya

  • Jangan ubah pengaturan default untuk konfigurasi GUC DateStyle ,TimeZone dan lc_time .
  • Jangan atur atau gunakan variabel lingkungan PGDATESTYLE dan PGTZ .
  • Jangan gunakan SET [SESSION|LOCAL] TIME ZONE ... .
  • Jika Anda bisa, atur zona waktu sistem ke UTC pada mesin yang menjalankan server Postgres, serta semua mesin yang menjalankan kode aplikasi yang terhubung dengannya.
  • Validasi bahwa driver database Anda (seperti konektor JDBC, atau driver Goddatabase/sql) berperilaku wajar saat klien berjalan di satu zona waktu dan server di zona waktu lainnya. Pastikan itu berfungsi dengan benar saat TimeZone non-UTC yang valid parameter disertakan dalam string koneksi.

Terakhir, perhatikan bahwa semua ini hanyalah pedoman dan dapat disesuaikan dengan kebutuhan Anda – tetapi pastikan Anda menyelidiki implikasinya terlebih dahulu.

Jenis dan Operator Asli

Jadi bagaimana tepatnya menggunakan tipe asli membantu menyederhanakan kode SQL? Berikut beberapa contoh.

Jenis Tanggal

Nilai tanggal type dapat dikurangi untuk memberikan interval diantara mereka. Anda juga dapat menambahkan bilangan bulat hari ke tanggal partikulat, atau menambahkan interval ke tanggal untuk memberikan timestamptz :

-- 10 days from now (outputs 2020-07-26)
SELECT now()::date + 10;
 
-- 10 days from now (outputs 2020-07-26 04:44:30.568847+00)
SELECT now() + '10 days'::interval;

-- days till christmas (outputs 161 days 14:06:26.759466)
SELECT '2020-12-25'::date - now();

-- the 10 longest courses
  SELECT name, end_date - start_date AS duration
    FROM courses
ORDER BY end_date - start_date DESC
   LIMIT 10;

Nilai jenis ini sebanding, itulah sebabnya Anda dapat memesan kueri terakhir sebelum end_date - start_date , yang memiliki jenis interval . Ini contoh lain:

-- certificates expiring within the next 7 days
SELECT name
  FROM certificates
 WHERE expiry_date BETWEEN now() AND now() + '7 days'::interval;

Jenis Stempel Waktu

Nilai tipe timestamptz juga dapat dikurangkan (untuk memberikan interval ),ditambahkan (ke interval untuk memberikan stempel waktu lagi ) dan dibandingkan.

-- difference of timestamps gives an interval
SELECT password_last_modified - created_at AS password_age
  FROM users;

-- can also use the age() function
SELECT age(password_last_modified, created_at) AS password_age
  FROM users;

Saat membahas topik, perhatikan bahwa ada 3 fungsi bawaan berbeda yang mengembalikan berbagai nilai "stempel waktu saat ini". Mereka sebenarnya mengembalikan hal yang berbeda:

-- transaction_timestamp() returns the timestampsz of the start of current transaction
-- outputs 2020-07-16 05:09:32.677409+00
SELECT transaction_timestamp();

-- statement_timestamp() returns the timestamptz of the start of the current statement
SELECT statement_timestamp();

-- clock_timestamp() returns the timestamptz of the system clock
SELECT clock_timestamp();

Ada juga alias untuk fungsi ini:

-- now() actually returns the start of the current transaction, which means it
-- does not change during the transaction
SELECT now(), transaction_timestamp();

-- transaction timestamp is also returned by these keyword-style constructs
SELECT CURRENT_DATE, CURRENT_TIMESTAMP, transaction_timestamp();

Jenis Interval

Nilai dengan tipe interval dapat digunakan sebagai tipe data kolom, dapat dibandingkan satu sama lain dan dapat ditambahkan ke (dan dikurangi dari) stempel waktu dan tanggal. Berikut beberapa contohnya:

-- interval-typed values can be stored and compared 
  SELECT num
    FROM passports
   WHERE valid_for > '10 years'::interval
ORDER BY valid_for DESC;

-- you can multiply them by numbers (outputs 4 years)
SELECT 4 * '1 year'::interval;

-- you can divide them by numbers (outputs 3 mons)
SELECT '1 year'::interval / 4;

-- you can add and subtract them (outputs 1 year 1 mon 6 days)
SELECT '1 year'::interval + '1.2 months'::interval;

Fungsi dan Konstruksi Lainnya

PostgreSQL juga dilengkapi dengan beberapa fungsi dan konstruksi berguna yang dapat digunakan untuk memanipulasi nilai jenis ini.

Ekstrak

Fungsi ekstrak dapat digunakan untuk mengambil bagian tertentu dari nilai yang diberikan, seperti bulan dari tanggal. Daftar lengkap bagian yang dapat diekstraksi didokumentasikan di sini. Berikut adalah beberapa contoh yang berguna dan tidak jelas:

-- years from an interval (outputs 2)
SELECT extract(YEARS FROM '1.5 years 6 months'::interval);

-- day of the week (0=Sun .. 6=Sat) from timestamp (outputs 4)
SELECT extract(DOW FROM now());

-- day of the week (1=Mon .. 7=Sun) from timestamp (outputs 4)
SELECT extract(ISODOW FROM now());

-- convert interval to seconds (outputs 86400)
SELECT extract(EPOCH FROM '1 day'::interval);

Contoh terakhir sangat berguna dalam kueri yang dijalankan oleh aplikasi, karena aplikasi dapat lebih mudah menangani interval sebagai nilai floating-point dari jumlah detik/menit/hari/dll.

Konversi Zona Waktu

Ada juga fungsi praktis untuk mengekspresikan timestamptz di zona waktu lain. Biasanya ini akan dilakukan dalam kode aplikasi – lebih mudah untuk mengujinya, dan mengurangi ketergantungan pada basis data zona waktu yang akan dirujuk oleh Postgresserver. Namun demikian, kadang-kadang dapat berguna:

-- convert timestamps to a different time zone
SELECT timezone('Europe/Helsinki', now());

-- same as before, but this one is a SQL standard
SELECT now() AT TIME ZONE 'Europe/Helsinki';

Mengonversi Ke Dan Dari Teks

Fungsi to_char (docs)dapat mengonversi tanggal, stempel waktu, dan interval menjadi teks berdasarkan format string – Postgres setara dengan fungsi C klasik strftime .

-- outputs Thu, 16th July
SELECT to_char(now(), 'Dy, DDth Month');

-- outputs 01 06 00 12 00 00
SELECT to_char('1.5 years'::interval, 'YY MM DD HH MI SS');

Untuk mengonversi dari teks ke tanggal gunakan to_date , dan untuk mengonversi teks menjadi stempel waktu gunakan to_timestamp . Perhatikan bahwa jika Anda menggunakan formulir yang tercantum di awal posting ini, Anda dapat menggunakan operator pemeran saja.

-- outputs 2000-12-25 15:42:50+00
SELECT to_timestamp('2000.12.25.15.42.50', 'YYYY.MM.DD.HH24.MI.SS');

-- outputs 2000-12-25
SELECT to_date('2000.12.25.15.42.50', 'YYYY.MM.DD');

Lihat dokumen untuk daftar lengkap pola format string.

Yang terbaik adalah menggunakan fungsi-fungsi ini untuk kasus-kasus sederhana. Untuk penguraian atau pemformatan yang lebih rumit, lebih baik mengandalkan kode aplikasi, yang (bisa dibilang) lebih baik untuk diuji unit.

Interface Dengan Kode Aplikasi

Terkadang tidak nyaman untuk meneruskan nilai tanggal/waktu/interval ke dan dari kode aplikasi, terutama ketika parameter terikat digunakan. Misalnya, biasanya lebih mudah untuk melewatkan interval sebagai bilangan bulat jumlah hari (atau jam, atau menit) daripada dalam format string. Ini juga lebih mudah dibaca dalam interval sebagai bilangan bulat/titik-mengambang dari hari (atau jam, atau menit, dll.).

make_interval fungsi dapat digunakan untuk membuat nilai interval dari bilangan integral dari nilai komponen (lihat dokumen di sini). to_timestamp fungsi yang kita lihat sebelumnya memiliki bentuk lain yang dapat membuat nilai atimestamptz dari waktu epoch Unix.

-- pass the interval as number of days from the application code
SELECT name FROM courses WHERE duration <= make_interval(days => $1);

-- pass timestamptz as unix epoch (number of seconds from 1-Jan-1970)
SELECT id FROM events WHERE logged_at >= to_timestamp($1);

-- return interval as number of days (with a fractional part)
SELECT extract(EPOCH FROM duration) / 60 / 60 / 24;

  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Batasan pemeriksaan PostgreSQL untuk kondisi kunci asing

  2. Apa sintaks PostgreSQL yang setara dengan Oracle CONNECT BY ... MULAI DENGAN?

  3. Format Angka dengan Koma di PostgreSQL

  4. Django + Psycopg2:InterfaceError:hanya protokol 3 yang didukung

  5. SETELAH LOGON(Oracle) memicu di PostgreSQL dengan ekstensi – login_hook