Cách tốt nhất để lưu trữ thời gian (hh: mm) trong cơ sở dữ liệu


100

Tôi muốn lưu trữ thời gian trong một bảng cơ sở dữ liệu nhưng chỉ cần lưu trữ giờ và phút. Tôi biết tôi chỉ có thể sử dụng DATETIME và bỏ qua các thành phần khác của ngày tháng, nhưng cách tốt nhất để làm điều này mà không cần lưu trữ nhiều thông tin hơn tôi thực sự cần là gì?


Tương đương với TIME cho máy chủ sql 2005 là bao nhiêu?
Erran Morad

@BoratSagdiyev không có cái nào trong SQL Server 2005, đó là lý do tại sao tôi đặt câu hỏi.
Matthew Dresser

Câu trả lời:


135

Bạn có thể lưu trữ nó dưới dạng số nguyên của số phút quá nửa đêm:

ví dụ.

0 = 00:00 
60 = 01:00
252 = 04:12

Tuy nhiên, bạn sẽ cần phải viết một số mã để tính lại thời gian, nhưng điều đó không quá phức tạp.


8
Sau đó, sử dụng DATEADD () để lấy lại thời gian thực. Ngay cả smallint cũng là đủ.
Joel Coehoorn

36
ở đó, thực hiện điều đó ... phút = dd% 60 và giờ = dd / 60 trên ints thực hiện thủ thuật.
Osama Al-Maadeed

2
Đó là một ý tưởng tuyệt vời, đơn giản và dễ hiểu. +1
whiskyierra

7
một nhược điểm nhỏ là thiếu khả năng đọc trong cơ sở dữ liệu
Jowen

chúng ta cần lưu trữ một số biến thể của ngày, giờ và phút; kiểu dữ liệu Thời gian trong SQL Server chỉ lên đến 23:59:59 nên chúng tôi không thể sử dụng. Lấy cảm hứng từ câu trả lời của bạn, chúng tôi quyết định có ba cột int cho ngày: giờ: phút để có độ linh hoạt tối đa. cảm ơn!
matao


15

DATETIME bắt đầu DATETIME kết thúc

Thay vào đó, tôi khuyên bạn nên sử dụng hai giá trị DATETIME , được gắn nhãn như event_startevent_end .

Thời gian là một công việc phức tạp

Hầu hết trên thế giới hiện đã áp dụng hệ thống đo lường dựa trên cây cối cho hầu hết các phép đo, đúng hay sai. Nhìn chung, điều này là tốt, vì ít nhất tất cả chúng ta đều có thể đồng ý rằng ag, là ml, là cm khối. Ít nhất là gần như vậy. Hệ thống số liệu có nhiều sai sót, nhưng ít nhất nó vẫn còn sai sót trên phạm vi quốc tế.

Tuy nhiên, với thời gian, chúng ta có; 1000 mili giây trong một giây, 60 giây đến một phút, 60 phút đến một giờ, 12 giờ cho mỗi nửa ngày, khoảng 30 ngày mỗi tháng thay đổi theo tháng và thậm chí cả năm được đề cập, mỗi quốc gia có thời gian bù đắp so với các quốc gia khác , cách thời gian được định dạng ở mỗi quốc gia khác nhau.

Đó là rất nhiều điều để hiểu, nhưng dài và ngắn của nó là không thể cho một kịch bản phức tạp như vậy để có một giải pháp đơn giản.

Một số góc có thể được cắt, nhưng có những nơi khôn ngoan hơn là không

Mặc dù câu trả lời hàng đầu ở đây gợi ý rằng bạn lưu trữ một số nguyên phút quá nửa đêm có vẻ hoàn toàn hợp lý, nhưng tôi đã học cách tránh làm như vậy một cách khó khăn.

Lý do để triển khai hai giá trị DATETIME là để tăng độ chính xác, độ phân giải và phản hồi.

Tất cả những điều này đều rất hữu ích khi thiết kế tạo ra kết quả không mong muốn.

Tôi có đang lưu trữ nhiều dữ liệu hơn yêu cầu không?

Ban đầu có thể có vẻ như nhiều thông tin đang được lưu trữ hơn tôi yêu cầu, nhưng có lý do chính đáng để thực hiện điều này.

Lưu trữ thông tin bổ sung này hầu như luôn giúp tôi tiết kiệm thời gian và công sức về lâu dài, bởi vì tôi chắc chắn nhận thấy rằng khi ai đó được cho biết điều gì đó đã diễn ra trong bao lâu, họ cũng sẽ muốn biết sự kiện diễn ra khi nào và ở đâu.

Đó là một hành tinh lớn

Trong quá khứ, tôi đã từng phạm tội khi phớt lờ rằng có những quốc gia khác trên hành tinh này ngoài quốc gia của tôi. Đó có vẻ là một ý tưởng hay vào thời điểm đó, nhưng điều này LUÔN LUÔN dẫn đến các vấn đề, đau đầu và lãng phí thời gian sau đó. LUÔN LUÔN xem xét tất cả các múi giờ.

C #

DateTime hiển thị độc đáo thành một chuỗi trong C #. Phương thức ToString (Định dạng chuỗi) nhỏ gọn và dễ đọc.

Ví dụ

new TimeSpan(EventStart.Ticks - EventEnd.Ticks).ToString("h'h 'm'm 's's'")

Máy chủ SQL

Ngoài ra, nếu bạn đang đọc cơ sở dữ liệu của mình tách biệt với giao diện ứng dụng của bạn, thì dateTimes rất vui khi đọc nhanh và thực hiện các phép tính trên chúng rất đơn giản.

Ví dụ

SELECT DATEDIFF(MINUTE, event_start, event_end)

Tiêu chuẩn ngày ISO8601

Nếu sử dụng SQLite thì bạn không có trường này, vì vậy thay vào đó hãy sử dụng trường Văn bản và lưu trữ nó ở định dạng ISO8601, ví dụ.

"2013-01-27T12: 30: 00 + 0000"

Ghi chú:

  • Điều này sử dụng đồng hồ 24 giờ *

  • Phần chênh lệch thời gian (hoặc +0000) của ISO8601 ánh xạ trực tiếp đến giá trị kinh độ của hệ thống định vị GPS (không tính đến tiết kiệm ánh sáng ban ngày hoặc trên toàn quốc).

Ví dụ

TimeOffset=(±Longitude.24)/360 

... trong đó ± đề cập đến hướng đông hoặc tây.

Do đó, sẽ đáng xem xét nếu nó có giá trị lưu trữ kinh độ, vĩ độ và độ cao cùng với dữ liệu. Điều này sẽ khác nhau trong ứng dụng.

  • ISO8601 là một định dạng quốc tế.

  • Wiki rất hữu ích để biết thêm chi tiết tại http://en.wikipedia.org/wiki/ISO_8601 .

  • Ngày và giờ được lưu trữ theo giờ quốc tế và độ lệch được ghi lại tùy thuộc vào nơi lưu trữ giờ trên thế giới.

Theo kinh nghiệm của tôi, luôn cần phải lưu trữ ngày và giờ đầy đủ, bất kể tôi có nghĩ là có thời điểm bắt đầu dự án hay không. ISO8601 là một cách làm rất tốt, bền vững trong tương lai.

Tư vấn thêm miễn phí

Nó cũng đáng để nhóm các sự kiện lại với nhau như một chuỗi. Ví dụ: nếu ghi lại một cuộc đua, toàn bộ sự kiện có thể được nhóm theo tay đua, đường đua, điểm kiểm tra mạch và điểm mạch điện.

Theo kinh nghiệm của tôi, việc xác định ai là người đã lưu trữ hồ sơ cũng rất khôn ngoan. Dưới dạng một bảng riêng biệt được điền thông qua trình kích hoạt hoặc dưới dạng một cột bổ sung trong bảng gốc.

Bạn càng đưa vào, bạn càng nhận ra nhiều

Tôi hoàn toàn hiểu mong muốn tiết kiệm không gian nhất có thể, nhưng tôi hiếm khi làm như vậy với cái giá phải trả là mất thông tin.

Một nguyên tắc chung với cơ sở dữ liệu là như tiêu đề đã nói, một cơ sở dữ liệu chỉ có thể cho bạn biết bao nhiêu là dữ liệu có và có thể rất tốn kém khi quay lại dữ liệu lịch sử, lấp đầy khoảng trống.

Giải pháp là làm cho nó chính xác lần đầu tiên. Điều này chắc chắn nói dễ hơn làm, nhưng bây giờ bạn nên có cái nhìn sâu sắc hơn về thiết kế cơ sở dữ liệu hiệu quả và sau đó có cơ hội cải thiện nhiều để thực hiện đúng ngay lần đầu tiên.

Thiết kế ban đầu của bạn càng tốt thì việc sửa chữa sau này càng ít tốn kém.

Tôi chỉ nói tất cả những điều này, bởi vì nếu tôi có thể quay ngược thời gian thì đó là điều tôi sẽ tự nhủ khi đến đó.


2
Nhận xét của bạn "Phần +0000 của ISO8601 ánh xạ trực tiếp đến độ trễ trong hệ thống định vị GPS" là sai. Nó đại diện cho sự bù đắp từ UTC
Gary Walker

10

Chỉ cần lưu trữ ngày giờ thông thường và bỏ qua mọi thứ khác. Tại sao phải dành thêm thời gian để viết mã tải một int, thao tác với nó và chuyển nó thành datetime, trong khi bạn chỉ có thể tải datetime?


3
Một lý do có thể là để tiết kiệm dung lượng trên ổ cứng - Một DATETIMEkiểu dữ liệu cần 4 byte để lưu trữ, trong khi một loại dữ liệu SMALLINTchẳng hạn, chỉ chiếm 1/4. Không có sự khác biệt lớn nếu bạn chỉ có một vài nghìn hàng, nhưng nếu bạn có hàng triệu hàng như nhiều công ty làm, thì tiết kiệm không gian của bạn sẽ đáng kể.
Sheridan

32
Có thể, nhưng hãy xem xét rằng 12 người đã trả lời câu hỏi này, mỗi người dành 15 phút để suy nghĩ về nó. Giả sử rằng họ đều là lập trình viên và kiếm được 50 đô la mỗi giờ. Với chi phí dành cho thời gian chỉ để suy nghĩ về vấn đề này, bạn có thể đã mua một ổ cứng 2TB mới sáng bóng để lưu trữ các byte thừa.
Seth

@Seth bạn nói đúng nếu chúng ta chỉ nói về các byte thừa. Nhưng đề xuất của bạn chỉ có thể là dự án nhỏ với 1-2 nhà phát triển. Nhưng nó sẽ là một dự án lớn với rất nhiều nhà phát triển (cộng với QA), nó sẽ là một vấn đề lớn khi một lập trình viên mới cố gắng tìm ra điều này. Than ôi, tất cả chúng ta đều phụ thuộc vào logic kinh doanh.
Hòa giải viên

Với các dịch vụ đám mây, việc tiết kiệm dung lượng ổ đĩa có thể rất quan trọng trong trường hợp bạn trả tiền cho mỗi byte đọc / xử lý.
RedShift

2
Một vấn đề thú vị khác là nếu bạn đang dựa vào thành phần thời gian, nó sẽ không tính đến các điều chỉnh tiết kiệm Ánh sáng ban ngày khi được sử dụng vào các thời điểm khác nhau trong năm.
Chris Seufert

4

vì bạn không đề cập đến nó một chút nếu bạn đang sử dụng SQL Server 2008, bạn có thể sử dụng kiểu dữ liệu thời gian, nếu không, hãy sử dụng phút kể từ nửa đêm


3

SQL Server thực sự lưu trữ thời gian dưới dạng phần nhỏ của một ngày. Ví dụ: 1 cả ngày = giá trị 1. 12 giờ là giá trị 0,5.

Nếu bạn muốn lưu trữ giá trị thời gian mà không sử dụng kiểu DATETIME, việc lưu trữ thời gian ở dạng thập phân sẽ phù hợp với nhu cầu đó, đồng thời giúp việc chuyển đổi sang DATETIME trở nên đơn giản.

Ví dụ:

SELECT CAST(0.5 AS DATETIME)
--1900-01-01 12:00:00.000

Lưu trữ giá trị dưới dạng DECIMAL (9,9) sẽ tiêu tốn 5 byte. Tuy nhiên, nếu độ chính xác không phải là quan trọng nhất, REAL sẽ chỉ tiêu tốn 4 byte. Trong cả hai trường hợp, phép tính tổng hợp (tức là thời gian trung bình) có thể dễ dàng được tính toán trên các giá trị số, nhưng không tính trên các loại Dữ liệu / Thời gian.


"SQL Server thực sự lưu trữ thời gian dưới dạng phần nhỏ của một ngày." Tôi nghĩ rằng nó lưu trữ các ngày kể từ (hoặc trước đó) 01-01-1900 trong 4 byte đầu tiên và thời gian, tính bằng mili giây, trong 4 byte thứ hai. (SmallDateTime sử dụng 2 byte cho mỗi byte với phạm vi ngày và phút hẹp hơn, thay vì mili giây cho thời gian)
Kristen

Tôi biết (chắc chắn 99,99%) rằng đối với thời gian trong ngày, nó là phân số.
graham.reeds

2

Tôi sẽ chuyển đổi chúng thành một số nguyên (HH * 3600 + MM * 60) và lưu trữ theo cách đó. Kích thước lưu trữ nhỏ và vẫn đủ dễ làm việc.


2

Nếu bạn đang sử dụng MySQL, hãy sử dụng loại trường TIME và chức năng liên quan đi kèm với TIME.

00:00:00 là định dạng thời gian unix tiêu chuẩn.

Nếu bạn phải xem lại và xem lại các bảng bằng tay, các số nguyên có thể khó hiểu hơn một dấu thời gian thực.


1

Hãy thử smalldatetime. Nó có thể không cung cấp cho bạn những gì bạn muốn nhưng nó sẽ giúp bạn trong những nhu cầu trong tương lai của bạn trong các thao tác ngày / giờ.


nếu @mdresser đang sử dụng <MSSQL 2005, máy chủ sẽ chạm mức "giá trị smalldatetime nằm ngoài phạm vi"
Fergus

1

Bạn có chắc mình sẽ chỉ cần giờ và phút không? Nếu bạn muốn làm bất cứ điều gì có ý nghĩa với nó (ví dụ như tính toán khoảng thời gian giữa hai điểm dữ liệu như vậy) thì không có thông tin về múi giờ và DST có thể cho kết quả không chính xác. Múi giờ có thể không áp dụng trong trường hợp của bạn, nhưng chắc chắn là DST.


1

Thay vì phút-quá-nửa đêm, chúng tôi lưu trữ nó dưới dạng đồng hồ 24 giờ, dưới dạng SMALLINT.

09:12 = 912 14:15 = 1415

khi chuyển đổi trở lại "dạng người có thể đọc được" chúng tôi chỉ chèn dấu hai chấm ":" hai ký tự từ bên phải. Dấu đệm bên trái với các số không nếu bạn cần. Lưu toán học theo từng cách và sử dụng ít hơn một vài byte (so với varchar), đồng thời thực thi rằng giá trị là số (thay vì chữ và số)

Tuy nhiên, khá ngốc nghếch ... lẽ ra đã có một kiểu dữ liệu TIME trong MS SQL trong nhiều năm rồi IMHO ...


Kristen hoàn toàn chính xác, nhưng chỉ khi giả định của cô ấy về giờ và phút chỉ đại diện cho một khoảng thời gian 24 giờ là đúng. 10 bit được yêu cầu để lưu trữ 0..1439 phút. Điều này có nghĩa là có thêm 6 bit có thể được sử dụng theo ý muốn của bạn, vì vậy bạn có thể thỏa sức sáng tạo. Tôi muốn đề nghị sử dụng 5 bit để lưu trữ các giờ bù đắp cho các múi giờ bạn đang ở, nhưng chỉ khi hỏi chỉ muốn lưu trữ một thời gian tối đa 23:59 (một phút đến nửa đêm)
WonderWorker

1

Những gì tôi nghĩ bạn đang yêu cầu là một biến sẽ lưu trữ phút dưới dạng một số. Điều này có thể được thực hiện với các loại biến số nguyên khác nhau:

SELECT 9823754987598 AS MinutesInput

Sau đó, trong chương trình của bạn, bạn có thể chỉ cần xem điều này ở dạng bạn muốn bằng cách tính:

long MinutesInAnHour = 60;

long MinutesInADay = MinutesInAnHour * 24;

long MinutesInAWeek = MinutesInADay * 7;


long MinutesCalc = long.Parse(rdr["MinutesInput"].toString()); //BigInt converts to long. rdr is an SqlDataReader.   


long Weeks = MinutesCalc / MinutesInAWeek;

MinutesCalc -= Weeks * MinutesInAWeek;


long Days = MinutesCalc / MinutesInADay;

MinutesCalc -= Days * MinutesInADay;


long Hours = MinutesCalc / MinutesInAnHour;

MinutesCalc -= Hours * MinutesInAnHour;


long Minutes = MinutesCalc;

Một vấn đề nảy sinh khi bạn yêu cầu sử dụng hiệu quả. Nhưng, nếu bạn thiếu thời gian thì chỉ cần sử dụng BigInt có thể null để lưu trữ giá trị phút của bạn.

Giá trị null nghĩa là thời gian chưa được ghi lại.

Bây giờ, tôi sẽ giải thích dưới dạng một chuyến đi khứ hồi đến không gian vũ trụ.

Thật không may, một cột bảng sẽ chỉ lưu trữ một loại duy nhất. Do đó, bạn sẽ cần tạo một bảng mới cho từng loại khi nó được yêu cầu.

Ví dụ:

  • Nếu MinutesInput = 0 .. 255 thì sử dụng TinyInt (Chuyển đổi như mô tả ở trên).

  • Nếu MinutesInput = 256 .. 131071 thì hãy sử dụng SmallInt (Lưu ý: Giá trị tối thiểu của SmallInt là -32,768. Do đó, hãy phủ định và thêm 32768 khi lưu trữ và truy xuất giá trị để sử dụng toàn dải trước khi chuyển đổi như trên).

  • Nếu MinutesInput = 131072 .. 8589934591 thì sử dụng Int (Lưu ý: Negate và thêm 2147483648 nếu cần).

  • Nếu MinutesInput = 8589934592 .. 36893488147419103231 sau đó sử dụng bigint (Lưu ý: Add và phủ nhận 9223372036854775808 khi cần thiết).

  • Nếu MinutesInput> 36893488147419103231 thì cá nhân tôi sẽ sử dụng VARCHAR (X) tăng X khi cần thiết vì char là một byte. Tôi sẽ phải xem lại câu trả lời này vào một ngày sau đó để mô tả điều này một cách đầy đủ (hoặc có thể một đồng nghiệp của stackoverflowee có thể hoàn thành câu trả lời này).

Vì mỗi giá trị chắc chắn sẽ yêu cầu một khóa duy nhất, nên hiệu quả của cơ sở dữ liệu sẽ chỉ rõ ràng nếu phạm vi giá trị được lưu trữ là sự kết hợp tốt giữa rất nhỏ (gần 0 phút) và rất cao (Lớn hơn 8589934591).

Cho đến khi các giá trị đang được lưu trữ thực sự đạt đến một số lớn hơn 36893488147419103231 thì bạn cũng có thể có một cột BigInt duy nhất để đại diện cho số phút của mình, vì bạn sẽ không cần lãng phí Int cho một số nhận dạng duy nhất và một số int khác để lưu trữ giá trị phút.


0

Theo Kristen, việc tiết kiệm thời gian ở định dạng UTC có thể giúp ích tốt hơn.

Đảm bảo rằng bạn đang sử dụng đồng hồ 24 giờ vì không có kinh tuyến AM hoặc PM nào được sử dụng trong UTC.

Thí dụ:

  • 4:12 sáng - 0412
  • 10:12 AM - 1012
  • 2:28 chiều - 1428
  • 11:56 PM - 2356

Nó vẫn thích sử dụng định dạng bốn chữ số tiêu chuẩn.


0

Lưu trữ ticksdưới dạng a long/ bigint, hiện được đo bằng mili giây. Giá trị được cập nhật có thể được tìm thấy bằng cách xem TimeSpan.TicksPerSecondgiá trị.

Hầu hết các cơ sở dữ liệu đều có kiểu DateTime tự động lưu trữ thời gian dưới dạng dấu tích phía sau, nhưng trong trường hợp của một số cơ sở dữ liệu như SqlLite, lưu trữ dấu tích có thể là một cách để lưu trữ ngày tháng.

Hầu hết các ngôn ngữ đều cho phép chuyển đổi dễ dàng từ TicksTimeSpanTicks .

Thí dụ

Trong C #, mã sẽ là:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Tuy nhiên, hãy lưu ý, bởi vì trong trường hợp của SqlLite, chỉ cung cấp một số nhỏ các loại khác nhau, đó là; INT, REALVARCHARNó sẽ là cần thiết để lưu trữ các số tick như là một chuỗi hoặc hai INTtế bào kết hợp. Điều này là do an INTlà số có dấu 32bit trong khiBIGINT là có dấu 64 bit.

Ghi chú

Tuy nhiên, sở thích cá nhân của tôi sẽ là lưu trữ ngày và giờ dưới dạng một ISO8601chuỗi.


-1

IMHO giải pháp tốt nhất là ở một mức độ nào đó phụ thuộc vào cách bạn lưu trữ thời gian trong phần còn lại của cơ sở dữ liệu (và phần còn lại của ứng dụng của bạn)

Cá nhân tôi đã làm việc với SQLite và cố gắng luôn sử dụng dấu thời gian unix để lưu trữ thời gian tuyệt đối, vì vậy khi xử lý thời gian trong ngày (như bạn yêu cầu), tôi thực hiện những gì Glen Solsberry viết trong câu trả lời của mình và lưu trữ số giây kể từ nửa đêm.

Khi thực hiện phương pháp tiếp cận chung này, mọi người (bao gồm cả tôi!) Đọc mã ít bị nhầm lẫn nếu tôi sử dụng cùng một tiêu chuẩn ở mọi nơi

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.