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

Trừ giờ cho hàm now ()

Câu trả lời cho timestamp

Bạn cần hiểu bản chất của các loại dữ liệu timestamp (timestamp without time zone ) và timestamptz (timestamp with time zone ). Nếu bạn không, hãy đọc phần này trước:

  • Bỏ qua hoàn toàn múi giờ trong Rails và PostgreSQL

AT TIME ZONE xây dựng các phép biến đổi timestamp đến timestamptz , gần như chắc chắn là nước đi sai lầm cho trường hợp của bạn:

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

Đầu tiên , nó giết chết hiệu suất. Áp dụng AT TIME ZONE vào cột eventtime làm cho biểu thức không phân biệt được . Postgres không thể sử dụng chỉ mục thuần túy trên eventtime . Nhưng ngay cả khi không có chỉ mục, các biểu thức sargable vẫn rẻ hơn. Điều chỉnh các giá trị bộ lọc thay vì thao tác mọi giá trị hàng.
Bạn có thể bù lại bằng một chỉ mục biểu thức phù hợp, nhưng dù sao thì đó có thể chỉ là sự hiểu nhầm và sai.

Điều gì xảy ra trong biểu thức đó?

  1. AT TIME ZONE 'CET' chuyển đổi timestamp giá trị eventtime đến timestamptz bằng cách thêm chênh lệch thời gian của múi giờ hiện tại của bạn. Khi sử dụng múi giờ tên (không phải là phần bù số hoặc chữ viết tắt), điều này cũng tính đến các quy tắc DST (giờ tiết kiệm ánh sáng ban ngày), vì vậy bạn sẽ nhận được mức chênh lệch khác cho dấu thời gian "mùa đông". Về cơ bản, bạn sẽ có câu trả lời cho câu hỏi:

    Dấu thời gian UTC tương ứng cho dấu thời gian nhất định trong múi giờ nhất định là gì?

    Khi hiển thị kết quả cho người dùng nó được định dạng là dấu thời gian cục bộ với thời gian bù theo múi giờ hiện tại của phiên. (Có thể giống hoặc không giống với giá trị được sử dụng trong biểu thức).

  2. Các ký tự chuỗi ở phía bên phải không có kiểu dữ liệu cho chúng, vì vậy kiểu được bắt nguồn từ phép gán trong biểu thức. Vì đó là timestamptz bây giờ, cả hai đều được truyền đến timestamptz , giả sử múi giờ hiện tại của phiên.

    Dấu thời gian UTC tương ứng cho dấu thời gian nhất định cho cài đặt múi giờ của phiên hiện tại là gì.

    Phần bù có thể thay đổi theo quy tắc DST.

Truyện ngắn , nếu bạn luôn luôn hoạt động với cùng múi giờ:CET hoặc 'Europe/Berlin' - điều tương tự đối với các dấu thời gian ngày nay, nhưng không phải đối với các dấu thời gian lịch sử hoặc (có thể) trong tương lai, bạn chỉ có thể cắt bỏ điểm mấu chốt.

Vấn đề thứ hai với biểu thức: BETWEEN hầu như luôn sai với timestamp các giá trị. Xem:

  • Tối ưu hóa GIỮA sao kê ngày
  • Tìm các phạm vi ngày trùng lặp trong 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à triển khai Postgres của tiêu chuẩn SQL CURRENT_TIMESTAMP . Cả hai đều trả về timestamptz (không phải timestamp !). Bạn có thể sử dụng một trong hai.
now()::date tương đương với CURRENT_DATE . Cả hai đều phụ thuộc vào cài đặt múi giờ hiện tại.

Bạn phải có chỉ mục có dạng:

CREATE INDEX foo ON t_el_eventlog(sourceid, eventtime)

Hoặc, để cho phép quét chỉ lập chỉ mục:

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

Nếu bạn hoạt động ở các múi giờ khác nhau, mọi thứ trở nên phức tạp hơn và bạn nên sử dụng timestamptz cho mọi thứ.

Thay thế cho timestamptz

Trước khi cập nhật câu hỏi, có vẻ như múi giờ là vấn đề. Khi xử lý các múi giờ khác nhau, "hôm nay" là một phụ thuộc hàm của múi giờ hiện tại. Mọi người có xu hướng quên điều đó.

Để chỉ làm việc với cài đặt múi giờ hiện tại của phiên, hãy sử dụng cùng một truy vấn như trên. Nếu thực hiện ở múi giờ khác, kết quả thực tế là sai. (Áp dụng cho cả những điều trên.)

Để đảm bảo kết quả chính xác cho một múi giờ nhất định (trong trường hợp của bạn là 'Châu Âu / Berlin') mà không nằm ngoài cài đặt múi giờ hiện tại của phiên, hãy sử dụng biểu thức này thay thế:

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

Lưu ý rằng AT TIME ZONE cấu trúc trả về timestamp cho timestamptz đầu vào và ngược lại.

Như đã đề cập ở phần đầu, tất cả các chi tiết đẫm máu ở đây:

  • Bỏ qua hoàn toàn các múi giờ trong Rails và 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. Làm cách nào để chỉ định mật khẩu cho 'psql' không tương tác?

  2. Máy chủ có đang chạy trên máy chủ localhost (::1) và chấp nhận kết nối TCP / IP trên cổng 5432 không?

  3. Làm cách nào để đăng nhập và xác thực vào Postgresql sau khi cài đặt mới?

  4. Làm cách nào để ghi các truy vấn PostgreSQL?

  5. Chèn nếu không tồn tại, nếu không trả về id trong postgresql