DateTime'ı depolamanın tercih edilen yolu


18

Tarih ve Saat bilgilerini birkaç şekilde saklayabiliriz. DateTime bilgilerini depolamak için en iyi yaklaşım nedir?

DateTime'ı kullanarak Tarih ve Saati 2 ayrı sütunda mı yoksa bir sütundasaklıyorsunuz ?

Bu yaklaşımın neden daha iyi olduğunu açıklayabilir misiniz?

(Referans için MySQL belgelerine bağlantı, soru geneldir, MySQL'e özgü değildir)
Tarih ve Saat türleri: Tarih ve Saat


3
Bu büyük ölçüde hangi veritabanı sistemini kullandığınıza bağlıdır. Değeri için: Oracle bunu bir sütun olarak (DATETIME veri türü olarak) yapmayı seçti, bu durumda yerleşik desteklerini kullanmak, bu bilgiyi NUMBER veri türü olarak 2 sütunda depolamaktan kesinlikle daha üstün olacaktır (yalnızca siz belirli bir sorgu için 1 bölüm gerekiyor ... tarih veya saat).
Kris Johnston

5
SQL Server için bölmenin tercih edilebileceği bir durum tarihe göre gruplama içindir. Bir dere agrega üzerine kompozit endeksi için bir çeşit olmadan kullanılamaz mümkün olacak date,time olan group by dateancak bir dizin için datetime olan group by cast(datetime as date)bu istenen sırayı sağlayacağını rağmen.
Martin Smith

1
Zaman değerlerine ilişkin herhangi bir matematikte tarih ve saat diliminin bilinmesi gerektiğini unutmayın - örneğin, iki kez arasındaki mesafe, o günün bir DST olayı içerip içermediğine, bazı günlerin 23 veya 25 saat içerdiğine ve artık saniye bulunduğuna bağlıdır.
Peteris

Yanıtlar:


23

Verilerin tek bir sütunda depolanması, ayrılmaz bir şekilde bağlantılı oldukları için tercih edilen yoldur. Zamandaki nokta, iki değil, tek bir bilgidir.

Birçok ürün tarafından "perde arkasında" kullanılan tarih / saat verisini depolamanın yaygın bir yolu, "tarih" ondalık değerin tamsayı kısmı ve "saat" in kesirli olduğu ondalık bir değere dönüştürmektir. değer. Böylece, 1900-01-01 00:00:00 0.0, 20 Eylül 2016 9:34:00 42631.39861 olarak saklanır. 42631, 1900-01-01'den bu yana geçen gün sayısıdır. .39861, gece yarısından bu yana geçen süredir. Bunu yapmak için doğrudan ondalık türü kullanmayın, açık bir tarih / saat türü kullanın; buradaki amacım sadece bir örnek.

Verilerin iki ayrı sütunda depolanması, belirli bir zaman noktasının depolanan değerden daha erken veya daha sonra olup olmadığını görmek istediğinizde her iki sütun değerini birleştirmeniz gerektiği anlamına gelir.

Değerleri ayrı ayrı saklarsanız, algılanması zor "hatalara" rastlarsınız. Örneğin aşağıdakileri ele alalım:

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

Yukarıdaki kodda, iki değerle dolduran bir test tablosu oluşturuyoruz, ardından bu verilere karşı basit bir sorgu gerçekleştiriyoruz. Birincisi SELECTher iki satırı da döndürür, ancak ikincisi SELECTyalnızca tek bir satır döndürür, bu da istenen sonuç olmayabilir:

resim açıklamasını buraya girin

Yorumlarda @ ypercube tarafından işaret edildiği gibi, değerlerin ayrı sütunlarda olduğu bir tarih / saat aralığını filtrelemenin doğru yolu:

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

Analiz amacıyla ayrılmış zaman bileşenine ihtiyacınız varsa , değerin zaman bölümü için hesaplanmış, kalıcı bir sütun eklemeyi düşünebilirsiniz:

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

SELECT *
FROM #dt;

resim açıklamasını buraya girin

Kalıcı sütun daha sonra günün saatine göre hızlı çeşitlere vb. İzin verecek şekilde dizine eklenebilir.

Tarih ve saati görüntüleme amacıyla iki alana ayırmayı düşünüyorsanız, biçimlendirmenin sunucuda değil istemcide yapılması gerektiğini fark etmelisiniz.


11

Diğer cevaplara karşı muhalif bir görüş sunacağım.

Hem tarih hem de saat bileşenleri birlikte gerekliyse, yani bir girdi birini içeriyor ancak diğerini içermiyorsa geçersizdir (veya birinde NULL ise ancak diğerinde değil), o zaman bunu tek bir sütunda saklamak, diğerlerinde verilen nedenlerden dolayı mantıklıdır. Yanıtlar.

Bununla birlikte, bir veya iki bileşenin ayrı ayrı isteğe bağlı olması söz konusu olabilir . Bu durumda, tek bir sütunda saklamak yanlış olur. Bunu yapmak, NULL değerlerini keyfi bir şekilde göstermeye zorlar, örneğin zamanı 00:00:00 olarak depolamak.

Burada bir çift örnek var:

  • Kilometre vergisi kesintileri için araç yolculuklarını kaydediyorsunuz. Yolculuğun kesin zamanını bilmek faydalı olacaktır, ancak bir çalışan bunu not etmediyse ve unutmuşsa, tarih hala kendi başına kaydedilmelidir (gerekli tarih, isteğe bağlı saat).

  • İnsanların öğle yemeğini ne zaman yediğini öğrenmek için bir anket yapıyorsunuz ve katılımcılardan tarihler de dahil olmak üzere öğle saatlerinin bir örneğiyle bir form doldurmalarını istiyorsunuz. Bazıları tarihi doldurmakla uğraşmaz ve verileri gerçekten önemsediğiniz zamanlar (isteğe bağlı tarih, gerekli zaman) atmak istemezsiniz.

Alternatif yaklaşımlar için bu soruya bakınız .


In RFC 3339 "Yerel bilinmeyen offset" kayıt için bir kongre var. "Bilinmeyen zaman" ın kullanım durumunu tamamen kapsadığını düşünmüyorum, ama yakın. Bir sonraki bölüm "niteliksiz yerel saat" daha da yakın, ama yine de yeterli değil.
geneorama

Evet, şuan yüzünden şemamı yeniden düzenleme variline bakıyorum. Araç kiralama durumuna geç. Bir kiralama şirketinden araba almak için - şirketin açık olması gerekir; böylece alıcı için bir tarih ve saat belirtirsiniz. Ancak, çoğu anahtar kutusu vardır; böylece saatler sonra düşersiniz. Konumu Pazar günleri kapalı Yani; bir ayrılma tarihi vardır; ama bir zaman değil. Bazı değerlerin gece yarısına kadar açık olması nedeniyle 0 değerinin (ör. 12:00) saklanması çalışmaz, bu diğer durumlarda geçerli bir değerdir.
Reece

5

Belirli bir işletme / uygulama talebi olmadıkça bunu her zaman tek bir sütun olarak saklamayı tercih ederim. Puanlarım aşağıdadır -

  • Zaman damgasından zaman ayıklamak sorun değil
  • İkisini birlikte depolayabiliyorsak neden sadece zaman için ekstra sütun eklemeliyiz?
  • Bunu önlemek için, sorgulama yaptığınızda her seferinde Tarih ve Saat ekleyin.

1
@a_horse_with_no_name burada bir noktaya değindi. Bence "datetimestamp ayıklanıyor damgası bir sorun değil" olarak adlandırılabilecek edilmelidir "ayıklanıyor damgası gelen zaman bir sorun değil" . "Zaman damgası" genellikle hem tarih hem de saat (ve genellikle saat dilimi) anlamına gelir.
ypercubeᵀᴹ

Evet, kabul et @ ypercubeᵀᴹ. Zaman damgası genellikle hem tarih hem de saat anlamına gelir. DateTimeStamp kelimesinden açıkça bahsettim, böylece herkes hem tarih hem de saat hakkında konuştuğumuzu anlayabilir. Ama sen de haklısın. Cevabı değiştirdi.
Ashwini Mohan

3

SQL Server'da DataTime'ı tek bir alan olarak saklamak en iyisidir. DataTime sütununda bir dizin oluşturursanız, Tarih araması ve DateTime araması olarak kullanılabilir. Bu nedenle, belirli bir tarih için var olan tüm kayıtları sınırlamanız gerekirse, özel bir şey yapmanıza gerek kalmadan dizini kullanabilirsiniz. Zaman bölümünü sorgulamanız gerekiyorsa, aynı dizini kullanamazsınız ve bu nedenle günün saati hakkında DateTime'dan daha fazla önem verdiğiniz bir iş durumunuz varsa, oluşturmanız gerekeceğinden ayrı olarak saklamanız gerekir. üzerinde bir endeks ve performansı artırmak.


1

Gerçekten de, bunun için standart bir çapraz DBMS türü olmaması üzücü (INT ve VARCHAR gibi tamsayılar ve dize değerleri için). Şimdiye kadar tanıştığım 2 çapraz veritabanı yaklaşımı, DataTime değerlerini ISO 8601 (daha uygun, insan tarafından okunabilir) standardına göre biçimlendirilmiş dizeler olarak saklamak için VARCHAR / CHAR sütunlarını kullanıyor ve bunları POSIX zaman damgaları (daha fazla depolanıyor) olarak saklamak için BIGINT kullanıyor verimli, hızlı, matematiksel olarak daha kolay manipüle edilebilir).


2
Evet var: timestampSQL standardının tanımladığı şey bu. Zaman damgalarını dizeler olarak saklamak çok kötü bir tavsiye
a_horse_with_no_name

0

Bir sürü şeyi okuduktan sonra, BIGINT'deki UTC Unix zamanı en uygun çözüm gibi görünüyor. Gerekirse saat dilimi depolama için VARCHAR'daki TZDB zaman çizelgesi kimliği. Birkaç argüman:

  1. TIMESTAMP ve DATETIME, arka planda karmaşık ve net görünmeyen bir dizi dikkat çekici dönüşüm yapıyor. Sunucu yerel saatten UTC'ye veya bazen sunucu zamanına veya zamanına değil. Her fonksiyon için bir sürü gizli yük.

  2. BIGINT (8kb), neredeyse MySQL tarafından iki INT + bir şey olarak depolanan xxxxxx.xxxxxx biçiminde depolama için gereken en az DECIMAL kadar hafif veya daha hafiftir . Ve yüzyıllar öncesini depolamak yeterlidir.

  3. Hemen hemen tüm büyük programlama dillerinde Unix zamanı ile çalışmak için standart fonksiyon kütüphaneleri bulunur.

  4. BIGINT ile matematik işlemleri, herhangi bir donanımdaki diğer her şeyden daha hızlı veya daha hızlı olmalıdır.

Elbette yukarıdakilerin hepsi büyük, uluslararası projelerle ilgilidir. Küçük bir şey için, seçilen çerçevenin varsayılan formatıyla devam etmek yeterince iyi görünüyor.


2
" arka planda bir sürü hileli dönüşüm yapıyor musunuz ... net değil " - hangi DBMS'den bahsediyorsunuz? Bir timestampsütun için (veritabanı katmanında) "dikkat çeken dönüşümler" gerçekleşmez timestamp with time zoneve bunun için kılavuzlarda iyi belgelenir ve açıklanır (en azından Oracle ve Postgres için)
a_horse_with_no_name

1
"Hemen hemen tüm büyük programlama dillerinde Unix zamanı ile çalışmak için standart fonksiyonların kütüphaneleri var." Ve yine de bigint kullanma tercihinizle, SQL /
DBMS'nin
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.