Bir veritabanında zamanı (ss: dd) depolamanın en iyi yolu


101

Zamanları bir veritabanı tablosunda saklamak istiyorum, ancak yalnızca saatleri ve dakikaları saklamam gerekiyor. DATETIME kullanabileceğimi ve tarihin diğer bileşenlerini görmezden gelebileceğimi biliyorum, ancak bunu gerçekte ihtiyacım olandan daha fazla bilgi depolamadan yapmanın en iyi yolu nedir?


Sql server 2005 için TIME eşdeğeri nedir?
Erran Morad

@BoratSagdiyev SQL Server 2005'te yok, bu yüzden soruyu sordum.
Matthew Dresser

Yanıtlar:


135

Bunu gece yarısını geçen dakika sayısının tamsayı olarak saklayabilirsiniz:

Örneğin.

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

Ancak zamanı yeniden oluşturmak için bir kod yazmanız gerekir, ancak bu zor olmamalıdır.


8
Daha sonra gerçek zamanları geri almak için DATEADD () kullanın. Ufak tefek bile yeterli olacaktır.
Joel Coehoorn

36
orada oldu, bunu yaptı ... dakika = gg% 60 ve saat = gg / 60 ints üzerinde hile yapıyor.
Usama Al-Maadeed

2
Bu harika, basit ve ileri doğru bir fikir. +1
viskisierra

7
küçük bir dezavantaj, veritabanındaki okunabilirliğin olmaması
Jowen

gün, saat ve dakika çeşitlerini saklamamız gerekir; SQL Server'daki Zaman veri türü yalnızca 23:59:59 kadar gittiğinden bunu kullanamadık. Cevabınızdan esinlenerek günler için üç int sütunu oluşturmaya karar verdik: maksimum esneklik için saat: dakika. Teşekkürler!
matao


14

DATETIME başlangıç ​​DATETIME bitiş

Bunun yerine event_start ve event_end gibi etiketlenmiş iki DATETIME değeri kullanmanızı rica ediyorum .

Zaman karmaşık bir iştir

Dünyanın çoğu, doğru ya da yanlış çoğu ölçüm için artık denery tabanlı metrik sistemi benimsemiştir. Bu genel olarak iyidir, çünkü en azından hepimiz ag'nin bir ml olduğu ve bir cm küp olduğu konusunda hemfikir olabiliriz. En azından yaklaşık olarak. Metrik sistemin birçok kusuru vardır, ancak en azından uluslararası olarak tutarlı bir şekilde kusurludur.

Ancak zamanla elimizde; Saniyede 1000 milisaniye, 60 saniyeden bir dakikaya, 60 dakikadan 1 saate, her yarım günde 12 saat, söz konusu aya ve hatta yıla göre değişen ayda yaklaşık 30 gün, her ülkenin diğerlerinden farklı zaman aralığı vardır , her ülkede zamanın biçimlendirme şekli farklılık gösterir.

Hazmetmesi gereken çok şey var, ancak bu kadar karmaşık bir senaryonun basit bir çözüme sahip olması uzun ve kısası imkansız.

Bazı köşeler kesilebilir, ancak yapmamanın daha akıllıca olduğu yerler vardır.

Buradaki en iyi yanıt, gece yarısından sonra bir tam sayı kaydetmenizin tamamen mantıklı görünebileceğini gösterse de, bunu zor yoldan yapmaktan kaçınmayı öğrendim.

İki DATETIME değerini uygulamanın nedenleri doğruluk, çözünürlük ve geri bildirimdeki artış içindir.

Bunların hepsi, tasarım istenmeyen sonuçlar doğurduğunda çok kullanışlıdır.

Gerekenden daha fazla veri depoluyor muyum?

Başlangıçta ihtiyaç duyduğumdan daha fazla bilgi depolanıyor gibi görünebilir, ancak bu isabeti almak için iyi bir neden var.

Bu ekstra bilgiyi saklamak neredeyse her zaman bana uzun vadede zaman ve emek tasarrufu sağlıyor, çünkü kaçınılmaz olarak birine bir şeyin ne kadar sürdüğü söylendiğinde, aynı zamanda olayın ne zaman ve nerede gerçekleştiğini de bilmek isteyeceklerini görüyorum.

Bu büyük bir gezegen

Geçmişte, bu gezegende benimkinden başka ülkeler olduğunu görmezden gelmekle suçluydum. O zamanlar iyi bir fikir gibi görünüyordu, ancak bu HER ZAMAN sorunlara, baş ağrısına ve daha sonra hattın aşağısında boşa zaman kaybına neden oldu. DAİMA tüm zaman dilimlerini göz önünde bulundurun.

C #

DateTime, C # 'da bir dizeye güzelce işlenir. ToString (dize Biçimi) yöntemi kompakttır ve okunması kolaydır.

Örneğin

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

SQL Server

Ayrıca, veritabanınızı uygulama arayüzünüzden ayrı olarak okuyorsanız, dateTimes bir bakışta okumaktan memnuniyet duyar ve bunların üzerinde hesaplamalar yapmak kolaydır.

Örneğin

SELECT DATEDIFF(MINUTE, event_start, event_end)

ISO8601 tarih standardı

SQLite kullanıyorsanız, buna sahip değilsiniz, bunun yerine bir Metin alanı kullanın ve ISO8601 formatında saklayın, örn.

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

Notlar:

  • Bu, 24 saatlik biçimi kullanır *

  • ISO8601'in saat farkı (veya +0000) kısmı, doğrudan bir GPS koordinatının boylam değeriyle eşleşir (gün ışığından yararlanma veya ülke genelini hesaba katmadan).

Örneğin

TimeOffset=(±Longitude.24)/360 

... burada ± doğu veya batı yönünü ifade eder.

Bu nedenle, verilerle birlikte boylam, enlem ve yüksekliği kaydetmeye değip değmeyeceğini düşünmeye değer. Bu, uygulamada değişiklik gösterecektir.

  • ISO8601, uluslararası bir formattır.

  • Vikipedi, daha fazla ayrıntı için http://en.wikipedia.org/wiki/ISO_8601 adresinde çok iyidir .

  • Tarih ve saat uluslararası saatte saklanır ve fark, saatin dünyanın neresinde saklandığına bağlı olarak kaydedilir.

Deneyimlerime göre, projeye başladığımda olduğunu düşünsem de, her zaman tam tarih ve saati kaydetme ihtiyacı vardır. ISO8601, bunu yapmanın çok iyi ve geleceğe yönelik bir yoludur.

Ücretsiz ek tavsiyeler

Ayrıca olayları bir zincir gibi bir arada gruplandırmaya değer. Örneğin, bir yarış kaydediliyorsa, tüm olay yarışçı, yarış_devre, devre denetimi noktaları ve devre turlarına göre gruplandırılabilir.

Tecrübelerime göre, kaydı kimin sakladığını belirlemek de akıllıca olacaktır. Tetikleyici aracılığıyla doldurulan ayrı bir tablo olarak veya orijinal tablo içinde ek bir sütun olarak.

Ne kadar çok koyarsan, o kadar çok dışarı çıkarsın

Alan konusunda olabildiğince ekonomik olma arzusunu tamamen anlıyorum, ancak nadiren bilgi kaybetme pahasına bunu yapardım.

Veritabanlarıyla ilgili temel bir kural, başlığın dediği gibi, bir veritabanı size yalnızca verilere sahip olduğu kadarını söyleyebilir ve geçmiş veriler arasında geri dönüp boşlukları doldurmak çok maliyetli olabilir.

Çözüm, onu ilk seferde doğru yapmaktır. Bunu söylemek kesinlikle yapmaktan daha kolaydır, ancak artık etkili veritabanı tasarımı hakkında daha derin bir kavrayışa sahip olmalısınız ve daha sonra bunu ilk seferde doğru yapma şansınız çok artmış olmalıdır.

İlk tasarımınız ne kadar iyi olursa, onarımlar daha sonra o kadar az maliyetli olacaktır.

Tüm bunları söylüyorum, çünkü zamanda geri dönebilseydim, oraya gittiğimde kendime söyleyeceğim şey buydu.


2
"ISO8601'in +0000 kısmı GPS koordinatındaki enlemle doğrudan eşleşiyor" yorumunuz yanlış. UTC'den farkı
Gary Walker

çok uzun soluklu ve bu soruyu nasıl yanıtladığını bile anlamıyorum. OP "8:30 am" gibi bir zaman depolamak istiyor ... Bunun için neden 2 tarih-saat alanına ihtiyaç duysun? 2 alan mı? Cevabınızın, zaman dilimlerinin yazılımda ve rastgele diğer DB tavsiyelerinde ne kadar zor olduğuna dair kişisel bir yansıma haline geldiğini hissediyorum.
Don Cheadle

10

Normal bir tarih saatini saklayın ve diğer her şeyi göz ardı edin. Bir tarih saatini yükleyebilecekken neden bir int yükleyen, onu işleyen ve bunu bir tarih saatine dönüştüren kod yazmak için fazladan zaman harcayasınız ki?


3
Olası bir neden, sabit sürücüde yer kazanmak olabilir - Bir DATETIMEveri türünün depolanması 4 bayt alırken SMALLINT, örneğin bir veri türünün yalnızca dörtte birini alır. Yalnızca birkaç bin satırınız varsa büyük bir fark yoktur, ancak birçok şirketin yaptığı gibi milyonlarca satırınız varsa, alan tasarrufunuz önemli olacaktır.
Sheridan

32
Muhtemelen, ancak 12 kişinin bu soruyu yanıtladığını düşünün, her birinin düşünmesi 15 dakika sürüyor. Hepsinin programcı olduklarını ve saatte 50 dolar kazandığını varsayalım. Sadece bu sorunu düşünmek için harcanan zamanın maliyeti ile, fazladan baytları depolamak için yepyeni bir 2 TB sabit disk satın alabilirdiniz.
Seth

@ Sadece fazladan baytlardan bahsedersek haklısın. Ancak öneriniz sadece 1-2 geliştiricili küçük bir proje ise olabilir. Ancak birçok geliştiricinin (artı kalite güvencesi) olduğu büyük bir proje olacak, yeni bir programcı bunu çözmeye çalıştığında büyük bir sorun olacaktır. Ne yazık ki, hepimiz iş mantığına bağlıyız.
Arabulucu

Bulut hizmetleriyle, okunan / işlenen bayt başına ödeme yaptığınız durumlarda disk alanından tasarruf etmek önemli olabilir.
RedShift

2
Bir başka eğlenceli sorun da, zaman bileşenine güveniyorsanız, yılın farklı zamanlarında kullanıldığında Günışığı tasarrufu ayarlamalarını hesaba katmayacaktır.
Chris Seufert

4

SQL Server 2008 üzerindeyseniz biraz bahsetmediğiniz için zaman veri türünü kullanabilirsiniz, aksi takdirde gece yarısından itibaren dakikaları kullanabilirsiniz


3

SQL Server, zamanı aslında bir günün kesirleri olarak depolar. Örneğin, 1 tam gün = 1 değeri. 12 saat, 0,5 değeridir.

Bir DATETIME türü kullanmadan zaman değerini saklamak istiyorsanız, zamanı ondalık biçimde saklamak bu ihtiyaca uygun olurken, aynı zamanda DATETIME dönüştürmeyi de basitleştirir.

Örneğin:

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

Değerin DECIMAL (9,9) olarak saklanması 5 bayt tüketir. Ancak, hassasiyet çok önemli değilse, GERÇEK sadece 4 bayt tüketir. Her iki durumda da, toplam hesaplama (yani ortalama süre) sayısal değerler üzerinden kolayca hesaplanabilir, ancak Veri / Zaman türlerinde hesaplanamaz.


"SQL Server, zamanı aslında bir günün kesirleri olarak depolar." 01 Ocak 1900'den beri (veya öncesinde) günleri ilk 4 baytta ve zamanı, milisaniye cinsinden, ikinci 4 baytta depoladığını düşünüyorum. (SmallDateTime, milisaniye yerine daha dar tarih aralığına ve dakikaya sahip her biri için 2 bayt kullanır)
Kristen

Biliyorum (% 99,99 eminim) günün saatinde kesirler olduğunu.
graham.reeds

2

Onları bir tam sayıya (HH * 3600 + MM * 60) dönüştürür ve bu şekilde saklardım. Küçük depolama boyutu ve yine de çalışmak için yeterince kolay.


2

MySQL kullanıyorsanız, bir alan türü olan TIME ve TIME ile birlikte gelen ilişkili işlevselliği kullanın.

00:00:00, standart unix saat formatıdır.

Geriye dönüp tabloları elle gözden geçirmek zorunda kalırsanız, tam sayılar gerçek bir zaman damgasından daha kafa karıştırıcı olabilir.


1

Smalldatetime deneyin. Size istediğinizi vermeyebilir ancak tarih / saat manipülasyonlarında gelecekteki ihtiyaçlarınızda size yardımcı olacaktır.


@mdresser <MSSQL 2005 kullanıyor olsaydı, sunucu "aralık dışı küçük tarih süresi değeri" ni kullanırdı
Fergus

1

Sadece saatlere ve dakikalara ihtiyacınız olacağından emin misiniz? Bununla anlamlı bir şey yapmak istiyorsanız (örneğin, bu tür iki veri noktası arasındaki zaman aralıklarını hesaplamak gibi), saat dilimleri ve DST hakkında bilgi sahibi olmamak yanlış sonuçlar verebilir. Saat dilimleri sizin durumunuzda geçerli olmayabilir, ancak DST kesinlikle geçerli olacaktır.


1

Dakikalar gece yarısını geçenler yerine SMALLINT olarak 24 saatlik zaman olarak saklıyoruz.

09:12 = 912 14:15 = 1415

"insan tarafından okunabilir biçime" geri dönüştürürken, sağdan iki karakter: "iki nokta ekleriz. Gerekirse sol tuşta sıfırlar. Matematiği her şekilde kaydeder ve daha az bayt kullanır (varchar ile karşılaştırıldığında), ayrıca değerin sayısal olmasını (alfanümerik değil) zorlar

Yine de oldukça saçma ... MS SQL'de bir yıl boyunca zaten bir TIME veri türü olmalıydı IMHO ...


Kristen kesinlikle haklıdır, ancak yalnızca tek bir 24 saatlik periyodu temsil eden saatler ve dakikalarla ilgili varsayımı doğruysa. 0..1439 dakika depolamak için 10 bit gereklidir. Bu, istediğiniz gibi kullanılabilecek ek 6 bit olduğu anlamına gelir, yani yaratıcı olmak için yer vardır. Bulunduğunuz saat dilimi için saat farkını saklamak için 5 bit kullanmanızı öneririm, ancak yalnızca soruyu soran kişi en fazla 23:59 (bir dakika ile gece yarısı arası) bir süre saklamak istiyorsa
WonderWorker

1

Bence istediğin şey dakikaları sayı olarak saklayacak bir değişkendir. Bu, çeşitli tam sayı değişkenleri ile yapılabilir:

SELECT 9823754987598 AS MinutesInput

Ardından, programınızda şunu hesaplayarak bunu istediğiniz biçimde görüntüleyebilirsiniz:

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;

Verimliliğin kullanılmasını talep ettiğinizde bir sorun ortaya çıkar. Ancak, zamanınız kısaysa , dakika değerinizi saklamak için boş atanabilir bir BigInt kullanın.

Null değeri, saatin henüz kaydedilmediği anlamına gelir.

Şimdi, uzaya gidiş-dönüş şeklinde açıklayacağım.

Ne yazık ki, bir tablo sütunu yalnızca tek bir türü depolayacaktır. Bu nedenle, gerektiği gibi her tür için yeni bir tablo oluşturmanız gerekecektir.

Örneğin:

  • MinutesInput = ise 0 .. 255 daha sonra kullanmak Tinyint (yukarıda tarif edildiği gibi dönüştürme).

  • MinutesInput = halinde 256 .. 131.071 sonra kullanmak SmallInt (: Bu nedenle, küçük tamsayı en az değeri 32,768 olan, negate ve depolama ve yukarıdaki gibi dönüştürmeden önce dizi yararlanmak için bir değer alırken 32768 ekleyin. Not).

  • MinutesInput = 131072 .. 8589934591 ise Int kullanın (Not: Negatif yapın ve gerektiği gibi 2147483648 ekleyin).

  • MinutesInput = 8589934592 .. 36893488147419103231 sonra kullanmak BigInt (: Gerektiğinde Ekle ve olumsuzlamak 9223372036854775808 Not).

  • MinutesInput> 36893488147419103231 ise, şahsen, karakter bayt olduğu için X'i gerektiği kadar artıran VARCHAR (X) kullanırdım. Bunu tam olarak açıklamak için bu yanıtı daha sonraki bir tarihte tekrar gözden geçirmem gerekecek (ya da belki de bir stackoverflowee bu yanıtı bitirebilir).

Her değer şüphesiz benzersiz bir anahtar gerektireceğinden, veritabanının verimliliği yalnızca depolanan değerlerin aralığı çok küçük (0 dakikaya yakın) ve çok yüksek (8589934591'den büyük) arasında iyi bir karışım ise görünür olacaktır.

Depolanan değerler gerçekte 36893488147419103231'den daha büyük bir sayıya ulaşana kadar, dakikalarınızı temsil edecek tek bir BigInt sütununuz olabilir, çünkü benzersiz bir tanımlayıcı için bir Int ve dakika değerini depolamak için başka bir int harcamanıza gerek kalmaz.


0

UTC formatında zaman tasarrufu, Kristen'in önerdiği gibi daha iyi yardımcı olabilir.

UTC'de meridyen AM veya PM kullanılmadığından 24 saatlik saati kullandığınızdan emin olun.

Misal:

  • 04:12 - 0412
  • 10:12 - 1012
  • 14:28 - 1428
  • 23:56 - 2356

Yine de standart dört basamaklı formatın kullanılması tercih edilir.


0

Mağaza ticksbir şekilde long/ ' bigintşu anda milisaniye cinsinden ölçülür. Güncellenen değer şuraya bakarak bulunabilir:TimeSpan.TicksPerSecond .

Çoğu veritabanında, saati otomatik olarak perde arkasındaki işaretler olarak depolayan bir DateTime türü vardır, ancak bazı veritabanlarında, örneğin SqlLite, tiklerin saklanması, tarihi saklamanın bir yolu olabilir.

Çoğu dil TicksTimeSpan→ ' den kolayca dönüştürmeye izin verir Ticks.

Misal

C # 'da kod şöyle olacaktır:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Ancak dikkat edin, çünkü yalnızca az sayıda farklı tür sunan SqlLite durumunda; INT, REALve VARCHAROnay sayısının bir dizi veya INTbirleştirilmiş iki hücre olarak saklanması gerekecektir . Bunun nedeni INT, 32 bit işaretli bir sayı iken BIGINT64 bit işaretli bir sayıdır.

Not

Ancak kişisel tercihim, tarih ve saati bir ISO8601dizge olarak saklamak olacaktır .


-1

IMHO en iyi çözümün ne olduğu, bir dereceye kadar veritabanının geri kalanında (ve uygulamanızın geri kalanında) zamanı nasıl depoladığınıza bağlıdır.

Şahsen ben SQLite ile çalıştım ve her zaman unix zaman damgalarını kullanmaya çalıştım mutlak zamanı saklamak , bu yüzden günün saatiyle uğraşırken (istediğiniz gibi) Glen Solsberry'nin cevabında yazdıklarını yapıyorum ve gece yarısından bu yana geçen saniye sayısını kaydediyorum

Bu genel yaklaşımı ele alırken, her yerde aynı standardı kullanırsam, kodu okuyan insanların (ben dahil!) Kafası daha az karışır.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.