Cara yang disukai untuk menyimpan DateTime

18

Kami dapat menyimpan informasi Tanggal dan Waktu dalam beberapa cara. Apa pendekatan terbaik untuk menyimpan informasi DateTime?

Menyimpan Tanggal dan Waktu dalam 2 kolom terpisah atau satu kolom menggunakan DateTime ?

Bisakah Anda menjelaskan mengapa pendekatan itu lebih baik?

(Tautan ke dokumen MySQL untuk referensi, pertanyaannya adalah umum, tidak khusus untuk MySQL) Jenis tanggal
dan Waktu: Tanggal dan Waktu

Julian
sumber
3
Itu sangat tergantung pada sistem basis data yang Anda gunakan. Untuk apa nilainya: Oracle memilih untuk melakukan ini sebagai satu kolom (sebagai datatype DATETIME), dalam hal ini, menggunakan dukungan bawaan mereka tentu akan lebih unggul daripada menyimpan informasi itu dalam 2 kolom sebagai NUMBER tipe data (bahkan jika Anda hanya perlu 1 bagian untuk permintaan yang diberikan ... tanggal atau waktu).
Kris Johnston
5
Untuk SQL Server satu kasus di mana pemisahan dapat lebih disukai adalah untuk pengelompokan berdasarkan tanggal. Agregat aliran akan dapat digunakan tanpa pengurutan untuk indeks komposit date,time dengan group by datetetapi tidak untuk indeks datetime dengan group by cast(datetime as date)meskipun itu akan memasok pesanan yang diinginkan.
Martin Smith
1
Perhatikan bahwa setiap matematika pada nilai Waktu memerlukan mengetahui tanggal dan zona waktu - mis. Jarak antara dua kali tergantung pada apakah hari itu berisi peristiwa DST, beberapa hari memiliki 23 atau 25 jam, dan detik kabisat juga ada.
Peteris

Jawaban:

23

Menyimpan data dalam satu kolom adalah cara yang lebih disukai, karena mereka terkait erat. Suatu titik waktu adalah satu informasi, bukan dua.

Cara umum menyimpan data tanggal / waktu, yang digunakan "di belakang layar" oleh banyak produk, adalah dengan mengubahnya menjadi nilai desimal di mana "tanggal" adalah bagian bilangan bulat dari nilai desimal, dan "waktu" adalah pecahan nilai. Jadi, 1900-01-01 00:00:00 disimpan sebagai 0,0 dan 20 September 2016 9:34:00 disimpan sebagai 42631.39861. 42631 adalah jumlah hari sejak 1900-01-01. 0,39861 adalah porsi waktu yang telah berlalu sejak tengah malam. Jangan menggunakan tipe desimal secara langsung untuk melakukan ini, gunakan tipe tanggal / waktu yang eksplisit; maksud saya di sini hanyalah ilustrasi.

Menyimpan data dalam dua kolom terpisah berarti Anda harus menggabungkan kedua nilai kolom setiap kali Anda ingin melihat apakah suatu titik waktu lebih awal atau lebih lambat dari nilai yang disimpan.

Jika Anda menyimpan nilai secara terpisah, Anda akan selalu menemukan "bug" yang sulit dideteksi. Ambil contoh berikut ini:

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

Dalam kode di atas, kami membuat tabel uji, mengisinya dengan dua nilai, lalu melakukan kueri sederhana terhadap data itu. Yang pertama SELECTmengembalikan kedua baris, namun yang kedua SELECThanya mengembalikan satu baris, yang mungkin bukan hasil yang diinginkan:

masukkan deskripsi gambar di sini

Cara yang benar untuk memfilter rentang tanggal / waktu di mana nilainya berada dalam kolom terpisah, seperti yang ditunjukkan oleh @ypercube dalam komentar, adalah:

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

Jika Anda memerlukan komponen waktu yang dipisahkan untuk keperluan analisis , Anda dapat mempertimbangkan menambahkan kolom yang dihitung, bertahan, untuk bagian waktu dari nilai:

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

SELECT *
FROM #dt;

masukkan deskripsi gambar di sini

Kolom yang bertahan kemudian dapat diindeks memungkinkan untuk jenis cepat, dll, berdasarkan waktu.

Jika Anda mempertimbangkan untuk membagi tanggal dan waktu menjadi dua bidang untuk tujuan tampilan, Anda harus menyadari bahwa pemformatan harus dilakukan pada klien, bukan server.

Max Vernon
sumber
11

Saya akan memberikan pendapat yang berbeda terhadap jawaban lainnya.

Jika kedua komponen tanggal dan waktu diharuskan bersama yaitu entri tidak valid jika berisi satu tetapi tidak yang lain (atau NULL dalam satu tetapi tidak yang lain), maka menyimpannya dalam satu kolom masuk akal karena alasan yang diberikan di lain jawaban.

Namun, mungkin menjadi kasus yang salah satu atau kedua komponen individual opsional. Dalam hal ini akan salah untuk menyimpannya dalam satu kolom. Melakukan hal itu akan memaksa Anda untuk mewakili nilai NULL dengan cara yang sewenang-wenang misalnya menyimpan waktu sebagai 00:00:00.

Berikut adalah beberapa contoh:

  • Anda merekam perjalanan kendaraan untuk pengurangan pajak jarak tempuh. Mengetahui waktu pasti perjalanan akan berguna tetapi jika seorang karyawan tidak mencatatnya dan lupa, tanggal tersebut harus tetap dicatat dengan sendirinya (tanggal yang diperlukan, waktu opsional).

  • Anda sedang melakukan survei untuk mengetahui jam berapa orang makan siang, dan Anda meminta peserta mengisi formulir dengan sampel waktu makan siang mereka, termasuk tanggal. Beberapa tidak repot mengisi tanggal, dan Anda tidak ingin membuang data karena ini adalah saat-saat Anda benar-benar peduli (tanggal opsional, waktu yang diperlukan).

Lihat pertanyaan terkait ini untuk pendekatan alternatif.

JBentley
sumber
Dalam RFC 3339 ada konvensi untuk merekam "offset lokal tidak dikenal". Saya tidak berpikir itu cukup mencakup kasus penggunaan "waktu tidak diketahui", tapi sudah dekat. Bagian selanjutnya "waktu lokal yang tidak memenuhi syarat" bahkan lebih dekat, tetapi sekali lagi itu tidak cukup.
geneorama
Ya, saya menatap laras refactoring skema saya karena ini sekarang. Ambil situasi persewaan mobil. Untuk mengambil mobil dari perusahaan rental - perusahaan harus terbuka; jadi Anda menentukan tanggal dan waktu untuk pengambilan. Namun, banyak yang memiliki kotak keydrop; jadi Anda mengantar setelah jam. Jadi jika lokasi ditutup pada hari Minggu; ada tanggal pengantaran; tapi bukan waktu. Menyimpan nilai 0 (mis. 12 pagi) tidak akan berfungsi karena beberapa lokasi buka hingga tengah malam, yang merupakan nilai yang valid dalam situasi lain.
Reece
5

Saya akan selalu lebih suka menyimpannya sebagai satu kolom kecuali ada permintaan bisnis / aplikasi tertentu. Di bawah ini adalah poin saya -

  • Mengekstrak waktu dari cap waktu tidak menjadi masalah
  • Mengapa menambahkan kolom tambahan hanya untuk waktu jika kita dapat menyimpan keduanya bersama-sama
  • Untuk menghindari menambahkan Tanggal dan Waktu setiap kali setiap kali Anda bertanya.
Ashwini Mohan
sumber
1
@a_horse_with_no_name ada benarnya di sini. Saya pikir "Ekstraksi timestamp dari datetimestamp bukan masalah" harus diulangi sebagai "Ekstraksi waktu dari timestamp bukan masalah" . "Stempel waktu" biasanya berarti tanggal dan waktu (dan biasanya zona waktu).
ypercubeᵀᴹ
Ya, setuju @ ypercubeᵀᴹ. Stempel waktu biasanya berarti tanggal dan waktu. Saya secara eksplisit menyebutkan kata DateTimeStamp, jadi siapa pun dapat mengerti bahwa kita berbicara tentang tanggal dan waktu keduanya. Tetapi Anda juga benar. Dimodifikasi jawabannya.
Ashwini Mohan
3

Dalam SQL Server yang terbaik adalah menyimpan DataTime sebagai satu bidang. Jika Anda membuat indeks pada kolom DataTime, itu dapat digunakan sebagai pencarian Tanggal dan sebagai pencarian DateTime. Karena itu jika Anda perlu membatasi semua catatan yang ada untuk tanggal tertentu, Anda masih dapat menggunakan indeks tanpa harus melakukan sesuatu yang istimewa. Jika Anda perlu meminta bagian waktu Anda tidak akan dapat menggunakan indeks yang sama dan oleh karena itu jika Anda memiliki kasus bisnis di mana Anda lebih peduli tentang waktu hari daripada DateTime, Anda harus menyimpannya secara terpisah karena Anda harus membuat indeks di atasnya dan meningkatkan kinerja.

Vladimir Oselsky
sumber
1

Memang, sangat disayangkan tidak ada tipe cross-DBMS standar untuk ini (seperti INT dan VARCHAR untuk bilangan bulat dan nilai string). 2 pendekatan lintas-basis data yang saya temui sejauh ini menggunakan kolom VARCHAR / CHAR untuk menyimpan nilai DataTime sebagai string yang diformat sesuai dengan standar ISO 8601 (lebih nyaman, dapat dibaca manusia) dan menggunakan BIGINT untuk menyimpannya sebagai cap waktu POSIX (disimpan lebih banyak efisien, lebih cepat, lebih mudah untuk memanipulasi secara matematis).

Ivan
sumber
2
Ya ada: timestampitulah yang mendefinisikan standar SQL. Menyimpan cap waktu sebagai string adalah saran yang sangat buruk
a_horse_with_no_name
0

Setelah membaca banyak hal, waktu UTC Unix di BIGINT tampaknya menjadi solusi optimal. TZDB timesone ID dalam VARCHAR untuk penyimpanan zona waktu jika diperlukan. Beberapa argumen:

  1. TIMESTAMP dan DATETIME melakukan banyak konversi menarik perhatian di latar belakang yang tampaknya kompleks dan tidak jelas. Server beralih dari waktu lokal ke UTC atau ke waktu server dan kembali, kadang-kadang, atau tidak. Banyak overhead tersembunyi untuk setiap fungsi.

  2. BIGINT (8kb) setidaknya sama ringan atau lebih ringan dari DECIMAL yang diperlukan untuk penyimpanan format xxxxxx.xxxxxx, yang secara praktis disimpan sebagai dua INT + sesuatu oleh MySQL . Dan itu cukup untuk menyimpan berabad-abad ke depan.

  3. Hampir semua bahasa pemrograman utama memiliki pustaka fungsi standar untuk bekerja dengan waktu Unix.

  4. Operasi matematika dengan BIGINT harus secepat atau lebih cepat dari apa pun pada perangkat keras apa pun.

Tentu saja semua hal di atas relevan untuk proyek internasional besar. Untuk sesuatu yang kecil, menggunakan format default dari kerangka yang dipilih tampaknya cukup baik.

Arthur Tarasov
sumber
2
" Apakah banyak konversi menarik perhatian di latar belakang yang tampaknya ... tidak jelas " - DBMS mana yang Anda bicarakan? Untuk timestampkolom tidak ada "konversi menarik perhatian" terjadi (pada lapisan basis data) dan untuk timestamp with time zoneini didokumentasikan dengan baik dan dijelaskan dalam manual (setidaknya untuk Oracle dan Postgres)
a_horse_with_no_name
1
"Hampir semua bahasa pemrograman utama memiliki pustaka fungsi standar untuk bekerja dengan waktu Unix." Namun Anda membuang semua perpustakaan dan fungsi tentang tanggal, datetimes, dan cap waktu yang dimiliki SQL / DBMS, dengan pilihan Anda menggunakan bigint ...
ypercubeᵀᴹ