PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Biểu diễn Ngày, Thời gian và Khoảng thời gian trong PostgreSQL

PostgreSQL đi kèm với một loạt các kiểu dữ liệu liên quan đến ngày và giờ được tích hợp sẵn. Tại sao bạn nên sử dụng chúng trên chuỗi hoặc số nguyên? Bạn nên chú ý điều gì khi sử dụng chúng? Đọc để tìm hiểu thêm về cách làm việc hiệu quả với các kiểu dữ liệu này trong Postgres.

Rất nhiều loại

Tiêu chuẩn SQL, tiêu chuẩn ISO 8601, danh mục tích hợp sẵn của PostgreSQL và khả năng tương thích ngược lại cùng nhau xác định rất nhiều kiểu dữ liệu và quy ước liên quan đến chồng chéo, có thể tùy chỉnh / thời gian gây nhầm lẫn tốt nhất. Sự kết hợp này thường tràn vào mã trình điều khiển cơ sở dữ liệu, mã ứng dụng, quy trình SQL và dẫn đến các lỗi nhỏ rất khó gỡ lỗi.

Mặt khác, việc sử dụng các kiểu gốc tích hợp sẵn sẽ đơn giản hóa các câu lệnh SQL và làm cho chúng dễ đọc và viết hơn nhiều và do đó, ít mắc lỗi hơn. và mã ứng dụng khác.

Lợi ích của các kiểu gốc làm cho việc xác định một tập hợp các quy tắc không gây đau đớn và thực thi chúng trong toàn bộ ứng dụng và cơ sở mã hoạt động là điều đáng giá. Đây là một tập hợp như vậy, sẽ cung cấp các mặc định hợp lý và một điểm khởi đầu phù hợp để tùy chỉnh thêm nếu cần.

Loại

Chỉ sử dụng 3 loại sau (mặc dù có nhiều loại):

  • ngày - một ngày cụ thể, không tính thời gian
  • timestamptz - một ngày và giờ cụ thể với độ phân giải micro giây
  • khoảng thời gian - khoảng thời gian với độ phân giải micro giây

Ba loại này kết hợp với nhau sẽ hỗ trợ hầu hết các trường hợp sử dụng ứng dụng. Nếu bạn không có nhu cầu cụ thể (như bảo tồn bộ nhớ), bạn chỉ nên sử dụng những loại này.

Ngày tháng đại diện cho một ngày không có thời gian và khá hữu ích trong thực tế (xem ví dụ bên dưới). Loại dấu thời gian là biến thể bao gồm thông tin múi giờ - không có thông tin múi giờ, chỉ có các biến toomany có thể ảnh hưởng đến việc diễn giải và trích xuất giá trị. Cuối cùng là khoảng thời gian đại diện cho các khoảng thời gian thấp nhất là một micro giây cho đến hàng triệu năm.

Chuỗi Literal

Chỉ sử dụng các biểu diễn theo nghĩa đen sau đây và sử dụng toán tử ép kiểu để giảm độ chi tiết mà không làm mất khả năng đọc:

  • '2012-12-25'::date - ISO 8601
  • '2012-12-25 13:04:05.123-08:00'::timestamptz - ISO 8601
  • '1 month 3 days'::interval - Định dạng truyền thống Postgres cho đầu vào ngắt quãng

Việc bỏ qua múi giờ khiến bạn tùy thuộc vào cài đặt múi giờ của máy chủ Postgres, cấu hình TimeZone có thể được đặt ở cấp cơ sở dữ liệu, cấp phiên, cấp vai trò hoặc trong chuỗi kết nối, cài đặt múi giờ của máy khách và nhiều yếu tố như vậy.

Trong khi truy vấn từ mã ứng dụng, hãy chuyển đổi các loại khoảng thời gian thành một đơn vị thích hợp (như ngày hoặc giây) bằng cách sử dụng extract hàm và đọc giá trị dưới dạng số nguyên hoặc giá trị thực.

Cấu hình và các cài đặt khác

  • Không thay đổi cài đặt mặc định cho cấu hình GUC DateStyle , TimeZonelc_time .
  • Không đặt hoặc sử dụng các biến môi trường PGDATESTYLEPGTZ .
  • Không sử dụng SET [SESSION|LOCAL] TIME ZONE ... .
  • Nếu bạn có thể, hãy đặt múi giờ hệ thống thành UTC trên máy chạy máy chủ Postgres, cũng như tất cả các máy chạy mã ứng dụng kết nối với nó.
  • Xác thực rằng trình điều khiển cơ sở dữ liệu của bạn (như trình kết nối JDBC hoặc trình điều khiển Godatabase / sql) hoạt động hợp lý trong khi máy khách đang chạy trên một vùng thời gian và máy chủ trên một vùng khác. Đảm bảo nó hoạt động chính xác khi một TimeZone không-UTC hợp lệ tham số được bao gồm trong chuỗi kết nối.

Cuối cùng, lưu ý rằng tất cả những điều này chỉ là hướng dẫn và có thể được điều chỉnh theo nhu cầu của bạn - nhưng hãy đảm bảo rằng bạn điều tra tác động của việc làm đó trước.

Loại Gốc và Toán tử

Vậy chính xác thì việc sử dụng các kiểu gốc giúp đơn giản hóa mã SQL như thế nào? Dưới đây là một vài ví dụ.

Loại Ngày

Giá trị của ngày loại có thể được trừ để cung cấp cho khoảng thời gian giữa họ. Bạn cũng có thể thêm một số nguyên ngày vào một ngày cụ thể, chọn một khoảng thời gian cho một ngày để đưa ra dấu thời gian :

-- 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;

Giá trị của các loại này có thể so sánh được, đó là lý do tại sao bạn có thể đặt hàng truy vấn cuối cùng trước end_date - start_date , có một loại khoảng thời gian . Đây là ví dụ điển hình:

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

Loại dấu thời gian

Giá trị của loại timestamptz cũng có thể được trừ (để cung cấp một khoảng thời gian ), đã thêm (vào một khoảng thời gian để cung cấp một timestamptz khác ) và được so sánh.

-- 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;

Trong khi ở trên chủ đề, hãy lưu ý rằng có 3 chức năng tích hợp khác nhau trả về các giá trị “dấu thời gian hiện tại” khác nhau. Họ thực sự trả về những thứ khác nhau:

-- 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();

Ngoài ra còn có các bí danh cho các chức năng này:

-- 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();

Loại khoảng thời gian

Các giá trị được nhập khoảng thời gian có thể được sử dụng làm kiểu dữ liệu cột, có thể được so sánh với bất kỳ loại dữ liệu nào khác và có thể được thêm vào (và trừ đi) dấu thời gian và ngày tháng. Dưới đây là một vài ví dụ:

-- 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;

Các hàm và cấu trúc khác

PostgreSQL cũng đi kèm với một số hàm và cấu trúc hữu ích có thể được sử dụng để thao tác các giá trị của các loại này.

Trích xuất

Hàm trích xuất có thể được sử dụng để truy xuất một phần cụ thể từ giá trị đã cho, chẳng hạn như tháng từ một ngày. Danh sách đầy đủ các phần có thể được trích xuất được tài liệu tại đây. Dưới đây là một số ví dụ hữu ích và không rõ ràng:

-- 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);

Ví dụ cuối cùng đặc biệt hữu ích trong các truy vấn do ứng dụng chạy, vì ứng dụng có thể dễ dàng xử lý khoảng thời gian dưới dạng giá trị dấu phẩy động của số giây / phút / ngày / v.v.

Chuyển đổi Múi giờ

Ngoài ra còn có một chức năng tiện dụng để thể hiện dấu thời gian trong một múi giờ khác. Thông thường, điều này sẽ được thực hiện trong mã ứng dụng - việc kiểm tra theo cách đó sẽ dễ dàng hơn và giảm sự phụ thuộc vào cơ sở dữ liệu múi giờ mà Postgresserver sẽ tham chiếu đến. Tuy nhiên, đôi khi nó có thể hữu ích:

-- 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';

Chuyển đổi sang và từ văn bản

Hàm to_char (docs) có thể chuyển đổi ngày, dấu thời gian và khoảng thời gian thành văn bản dựa trên chuỗi định dạng – Postgres tương đương với hàm C cổ điển 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');

Để chuyển đổi từ văn bản sang ngày tháng, hãy sử dụng to_date và để chuyển đổi văn bản thành dấu tối ưu, hãy sử dụng to_timestamp . Lưu ý rằng nếu bạn sử dụng các biểu mẫu được liệt kê ở đầu bài đăng này, bạn chỉ có thể sử dụng toán tử truyền thay thế.

-- 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');

Xem tài liệu để biết danh sách đầy đủ các mẫu chuỗi định dạng.

Tốt nhất là sử dụng các chức năng này cho các trường hợp đơn giản. Đối với việc phân tích hoặc định dạng phức tạp hơn, tốt hơn nên dựa vào mã ứng dụng, mã này có thể (được cho là) ​​được kiểm tra đơn vị tốt hơn.

Giao diện với mã ứng dụng

Đôi khi không thuận tiện để chuyển các giá trị date / timestamptz / khoảng thời gian sang và từ mã ứng dụng, đặc biệt khi các tham số ràng buộc được sử dụng. Ví dụ, sẽ thuận tiện hơn khi chuyển một khoảng thời gian dưới dạng số nguyên ngày (hoặc giờ hoặc phút) hơn là ở định dạng chuỗi. Cũng dễ đọc hơn trong một khoảng thời gian dưới dạng số nguyên / dấu phẩy động, ngày (hoặc giờ hoặc phút, v.v.).

make_interval hàm có thể được sử dụng để tạo một giá trị khoảng từ một số tích phân của các giá trị thành phần (xem tài liệu tại đây). to_timestamp hàm mà chúng ta đã thấy trước đó có một dạng khác có thể tạo giá trị atimestamptz từ thời gian kỷ nguyên 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. Khối mã ẩn danh PL / pgSQL

  2. Di chuyển cơ sở dữ liệu PostgreSQL từ On-Prem sang đám mây bằng AWS RDS

  3. Chọn ba giá trị hàng đầu trong mỗi nhóm

  4. Cách trả lại id trên Phụ trang với Ibatis (với từ khóa QUAY LẠI)

  5. PostgreSql CHÈN TỪ CHỌN ID QUAY LẠI