Cách ưa thích để lưu trữ DateTime


18

Chúng tôi có thể lưu trữ thông tin Ngày và Giờ theo một số cách. Cách tiếp cận tốt nhất để lưu trữ thông tin DateTime là gì?

Lưu trữ Ngày và Giờ trong 2 cột riêng biệt hoặc một cột bằng DateTime ?

Bạn có thể giải thích tại sao cách tiếp cận đó tốt hơn?

(Liên kết đến tài liệu MySQL để tham khảo, câu hỏi chung chung, không dành riêng cho MySQL) Các loại
Ngày và Giờ: Ngày và Giờ


3
Điều đó phần lớn phụ thuộc vào hệ thống cơ sở dữ liệu bạn đang sử dụng. Vì giá trị của nó: Oracle đã chọn làm điều này dưới dạng một cột (dưới dạng kiểu dữ liệu DATETIME), trong trường hợp đó, sử dụng hỗ trợ tích hợp của họ chắc chắn sẽ vượt trội hơn so với việc lưu trữ thông tin đó trong 2 cột dưới dạng SỐ kiểu dữ liệu (ngay cả khi bạn chỉ cần 1 phần cho một truy vấn nhất định ... ngày hoặc thời gian).
Kris Johnston

5
Đối với SQL Server, một trường hợp phân tách có thể được ưu tiên là nhóm theo ngày. Một tổng dòng sẽ có thể được sử dụng mà không có một loại đối với chỉ số tổng hợp trên date,time với group by datenhưng không phải cho một chỉ mục trên datetime với group by cast(datetime as date)mặc dù nó sẽ cung cấp thứ tự mong muốn.
Martin Smith

1
Xin lưu ý rằng bất kỳ phép toán nào trên các giá trị Thời gian đều yêu cầu biết ngày và múi giờ - ví dụ: khoảng cách giữa hai lần phụ thuộc vào thời điểm ngày đó có sự kiện DST, một số ngày có 23 hoặc 25 giờ và giây nhuận cũng tồn tại.
Peteris

Câu trả lời:


23

Lưu trữ dữ liệu trong một cột là cách ưa thích, vì chúng được liên kết chặt chẽ. Một điểm trong thời gian là một mẩu thông tin, không phải hai.

Một cách phổ biến để lưu trữ dữ liệu ngày / giờ, được sử dụng "đằng sau hậu trường" của nhiều sản phẩm, bằng cách chuyển đổi nó thành giá trị thập phân trong đó "ngày" là phần nguyên của giá trị thập phân và "thời gian" là phân số giá trị. Vì vậy, 1900-01-01 00:00:00 được lưu trữ là 0.0 và ngày 20 tháng 9 năm 2016 9:34:00 được lưu trữ dưới dạng 42631,39861. 42631 là số ngày kể từ 1900-01-01. .39861 là phần thời gian trôi qua kể từ nửa đêm. Không sử dụng loại thập phân trực tiếp để làm điều này, sử dụng loại ngày / giờ rõ ràng; quan điểm của tôi ở đây chỉ là một minh họa.

Lưu trữ dữ liệu trong hai cột riêng biệt có nghĩa là bạn sẽ cần kết hợp cả hai giá trị cột bất cứ lúc nào bạn muốn xem liệu một thời điểm nhất định sớm hơn hoặc muộn hơn giá trị được lưu trữ.

Nếu bạn lưu trữ các giá trị một cách riêng biệt, bạn sẽ luôn gặp phải các "lỗi" rất khó phát hiện. Lấy ví dụ như sau:

IF OBJECT_ID('tempdb..#DT') IS NOT NULL
DROP TABLE #DT;
CREATE TABLE #DT
(
    dt_value DATETIME NOT NULL
    , d_value DATE NOT NULL
    , t_value TIME(0) NOT NULL
);


DECLARE @d DATETIME = '2016-09-20 09:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

SET @d = '2016-09-20 11:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.dt_value >= '2016-07-01 11:00:00';

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.d_value >= CONVERT(DATE, '2016-07-01')
    AND dt.t_value >= CONVERT(TIME(0), '11:00:00');

Trong đoạn mã trên, chúng tôi đang tạo một bảng thử nghiệm, điền vào đó hai giá trị, sau đó thực hiện một truy vấn đơn giản đối với dữ liệu đó. Hàng đầu tiên SELECTtrả về cả hai hàng, tuy nhiên hàng thứ hai SELECTchỉ trả về một hàng duy nhất, có thể không phải là kết quả mong muốn:

nhập mô tả hình ảnh ở đây

Cách chính xác để lọc phạm vi ngày / thời gian trong đó các giá trị nằm trong các cột riêng biệt, như được chỉ ra bởi @ypercube trong các nhận xét, là:

WHERE dt.d_value > CONVERT(DATE, '2016-07-01') /* note there is no time component here */
    OR (
        dt.d_value = CONVERT(DATE, '2016-07-01') 
        AND dt.t_value >= CONVERT(TIME(0), '11:00:00')
    )

Nếu bạn cần tách thành phần thời gian cho mục đích phân tích , bạn có thể xem xét thêm cột được tính toán, duy trì, cho phần thời gian của giá trị:

ALTER TABLE #DT
ADD dt_value_time AS CONVERT(TIME(0), dt_value) PERSISTED;

SELECT *
FROM #dt;

nhập mô tả hình ảnh ở đây

Cột tồn tại sau đó có thể được lập chỉ mục cho phép sắp xếp nhanh, v.v., theo thời gian trong ngày.

Nếu bạn đang xem xét phân chia ngày và thời gian thành hai trường cho mục đích hiển thị, bạn nên nhận ra rằng định dạng nên được thực hiện tại máy khách, không phải máy chủ.


11

Tôi sẽ cung cấp một ý kiến ​​không đồng tình với các câu trả lời khác.

Nếu cả hai thành phần ngày và thời gian được yêu cầu cùng nhau, tức là một mục không hợp lệ nếu nó chứa một nhưng không phải là một (hoặc là NULL trong một nhưng không phải là một), thì lưu trữ nó trong một cột duy nhất có ý nghĩa cho các lý do được đưa ra khác câu trả lời.

Tuy nhiên, nó có thể là trường hợp một hoặc cả hai thành phần là tùy chọn riêng . Trong trường hợp đó, việc lưu trữ nó trong một cột sẽ không chính xác. Làm như vậy sẽ buộc bạn phải biểu diễn các giá trị NULL theo cách tùy ý, ví dụ như lưu trữ thời gian là 00:00:00.

Dưới đây là một vài ví dụ:

  • Bạn đang ghi lại hành trình xe để khấu trừ thuế số dặm. Biết thời gian chính xác của hành trình sẽ hữu ích nhưng nếu một nhân viên không ghi lại và quên, ngày vẫn phải được ghi lại (ngày bắt buộc, thời gian tùy chọn).

  • Bạn đang thực hiện một cuộc khảo sát để tìm hiểu thời gian mọi người ăn bữa trưa của họ và bạn yêu cầu những người tham gia hoàn thành một mẫu với mẫu thời gian ăn trưa của họ, bao gồm cả ngày. Một số người không bận tâm điền vào ngày và bạn không muốn loại bỏ dữ liệu vì đó là thời gian bạn thực sự quan tâm (ngày tùy chọn, thời gian bắt buộc).

Xem câu hỏi liên quan này cho các phương pháp thay thế.


Trong RFC 3339 có một quy ước để ghi "bù cục bộ không xác định". Tôi không nghĩ nó hoàn toàn bao gồm trường hợp sử dụng "thời gian không xác định", nhưng nó đã kết thúc. Phần tiếp theo "giờ địa phương không đủ tiêu chuẩn" thậm chí còn gần hơn, nhưng một lần nữa, nó không đủ.
genorama

Vâng, tôi đang nhìn chằm chằm vào thùng tái cấu trúc lược đồ của mình vì điều này ngay bây giờ. Đi một tình huống cho thuê xe. Để chọn một chiếc xe từ một công ty cho thuê - công ty cần phải được mở; vì vậy bạn chỉ định ngày và giờ cho xe bán tải. Tuy nhiên, nhiều người có hộp phím; vì vậy bạn thả ra sau giờ. Vì vậy, nếu địa điểm đóng cửa vào Chủ nhật; có một ngày thả nhưng không phải là một thời gian Lưu trữ giá trị 0 (ví dụ 12 giờ sáng) sẽ không hoạt động vì một số vị trí mở cửa đến nửa đêm, đây là giá trị hợp lệ trong các tình huống khác.
lừa

5

Tôi sẽ luôn thích lưu trữ dưới dạng một cột trừ khi có một số nhu cầu kinh doanh / ứng dụng cụ thể. Dưới đây là điểm của tôi -

  • Trích xuất thời gian từ dấu thời gian không phải là vấn đề
  • Tại sao phải thêm cột thêm cho thời gian nếu chúng ta có thể lưu trữ cả hai cùng nhau
  • Để tránh thêm Ngày và Giờ mỗi lần bạn truy vấn.

1
@a_horse_with_no_name có một điểm ở đây. Tôi nghĩ rằng "Trích xuất dấu thời gian từ datetimestamp không phải là vấn đề" nên được viết lại là "Trích xuất thời gian từ dấu thời gian không phải là vấn đề" . "Dấu thời gian" thường có nghĩa là cả ngày và giờ (và thường là múi giờ).
ypercubeᵀᴹ

Có, đồng ý @ ypercubeᵀᴹ. Dấu thời gian thường có nghĩa là cả ngày và thời gian. Tôi đã đề cập rõ ràng từ DateTimeStamp, vì vậy bất cứ ai cũng có thể hiểu rằng chúng ta đang nói về cả ngày và thời gian. Nhưng bạn cũng đúng. Sửa đổi câu trả lời.
Ashwini Mohan

3

Trong SQL Server, tốt nhất là lưu trữ DataTime dưới dạng một trường. Nếu bạn tạo một chỉ mục trên cột DataTime, nó có thể được sử dụng làm tìm kiếm Ngày và tìm kiếm DateTime. Do đó, nếu bạn cần giới hạn tất cả các bản ghi tồn tại cho ngày cụ thể, bạn vẫn có thể sử dụng chỉ mục mà không phải làm gì đặc biệt. Nếu bạn cần truy vấn phần thời gian, bạn sẽ không thể sử dụng cùng một chỉ mục và do đó nếu bạn gặp trường hợp kinh doanh mà bạn quan tâm nhiều hơn về thời gian trong ngày so với DateTime, bạn nên lưu trữ riêng vì bạn sẽ cần tạo một chỉ số về nó và cải thiện hiệu suất.


1

Thật vậy, điều đáng tiếc là không có loại DBMS chéo tiêu chuẩn cho điều này (như INT và VARCHAR dành cho số nguyên và giá trị chuỗi). Hai cách tiếp cận cơ sở dữ liệu chéo mà tôi đã gặp cho đến nay là sử dụng các cột VARCHAR / CHAR để lưu trữ các giá trị DataTime dưới dạng các chuỗi được định dạng theo tiêu chuẩn ISO 8601 (thuận tiện hơn, dễ đọc hơn cho con người) và sử dụng BIGINT để lưu trữ chúng dưới dạng dấu thời gian POSIX (được lưu trữ nhiều hơn hiệu quả, nhanh hơn, dễ dàng hơn để thao tác toán học).


2
Có, đó là timestampnhững gì tiêu chuẩn SQL định nghĩa. Lưu dấu thời gian dưới dạng chuỗi là một lời khuyên rất tệ
a_horse_with_no_name

0

Sau khi đọc một loạt các công cụ, thời gian Unix UTC trong BIGINT dường như là giải pháp tối ưu. TZDB timesone ID trong VARCHAR để lưu trữ múi giờ nếu cần thiết. Một vài lập luận:

  1. TIMESTAMP và DATETIME thực hiện một loạt các chuyển đổi phô trương trong nền có vẻ phức tạp và không rõ ràng. Máy chủ chuyển từ giờ địa phương sang UTC hoặc sang thời gian máy chủ và ngược lại, đôi khi, hoặc không. Một loạt các chi phí ẩn cho mọi chức năng.

  2. BIGINT (8kb) ít nhất là nhẹ hoặc nhẹ hơn DECIMAL cần thiết cho lưu trữ định dạng xxxxxx.xxxxxx, thực tế được lưu trữ dưới dạng hai INT + một thứ gì đó bởi MySQL . Và nó đủ để lưu trữ hàng thế kỷ phía trước.

  3. Khá nhiều ngôn ngữ lập trình chính có thư viện các hàm tiêu chuẩn để hoạt động với thời gian Unix.

  4. Các phép toán với BIGINT phải nhanh hoặc nhanh hơn bất kỳ thứ gì khác trên bất kỳ phần cứng nào.

Tất nhiên tất cả những điều trên có liên quan đến các dự án lớn, quốc tế. Đối với một cái gì đó nhỏ, đi với định dạng mặc định của khung đã chọn dường như là đủ tốt.


2
" thực hiện một loạt các chuyển đổi phô trương trong nền dường như ... không rõ ràng " - bạn đang nói về DBMS nào? Đối với một timestampcột không có "chuyển đổi phô trương" xảy ra (ở lớp cơ sở dữ liệu) và đối với timestamp with time zoneđiều này được ghi lại và giải thích rõ trong hướng dẫn sử dụng (ít nhất là cho Oracle và Postgres)
a_horse_with_no_name

1
"Khá nhiều tất cả các ngôn ngữ lập trình chính đều có thư viện các hàm tiêu chuẩn để hoạt động với thời gian Unix." Tuy nhiên, bạn loại bỏ tất cả các thư viện và hàm về ngày, thời gian và dấu thời gian mà SQL / DBMS có, với sự lựa chọn của bạn là sử dụng bigint ...
ypercubeᵀᴹ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.