วิธีที่ต้องการในการจัดเก็บ DateTime


18

เราสามารถจัดเก็บข้อมูลวันที่และเวลาได้หลายวิธี วิธีที่ดีที่สุดในการจัดเก็บข้อมูล DateTime คืออะไร

การจัดเก็บวันที่และเวลาใน2 คอลัมน์แยกกันหรือหนึ่งคอลัมน์โดยใช้ DateTime ?

คุณช่วยอธิบายได้ไหมว่าทำไมวิธีการนั้นถึงดีกว่า?

(ลิงก์ไปยังเอกสาร MySQL สำหรับการอ้างอิงคำถามทั่วไปไม่ใช่เฉพาะ MySQL)
ประเภทวันที่และเวลา : วันที่และเวลา


3
นั้นขึ้นอยู่กับระบบฐานข้อมูลที่คุณใช้เป็นส่วนใหญ่ สำหรับสิ่งที่มีค่า: ออราเคิลเลือกที่จะทำสิ่งนี้เป็นหนึ่งคอลัมน์ (เป็นประเภทข้อมูล DATETIME) ซึ่งในกรณีนี้การใช้การสนับสนุนที่มีอยู่แล้วนั้นจะเหนือกว่าการจัดเก็บข้อมูลในคอลัมน์ 2 คอลัมน์เป็นประเภทข้อมูล NUMBER ต้องการ 1 ส่วนสำหรับข้อความค้นหาที่กำหนด ... วันที่หรือเวลา)
กริชจอห์นสตัน

5
สำหรับ SQL Server กรณีหนึ่งที่สามารถแยกออกได้คือการจัดกลุ่มตามวันที่ การรวมกระแสจะสามารถใช้งานได้โดยไม่มีการเรียงลำดับสำหรับดัชนีคอมโพสิตdate,time ด้วยgroup by dateแต่ไม่ใช้สำหรับดัชนีบนdatetime ด้วยgroup by cast(datetime as date)แม้ว่าจะให้ลำดับที่ต้องการ
Martin Smith

1
โปรดทราบว่าการคำนวณทางคณิตศาสตร์เกี่ยวกับค่าเวลาจำเป็นต้องทราบวันที่และเขตเวลา - เช่นระยะห่างระหว่างสองครั้งขึ้นอยู่กับว่าวันนั้นมีเหตุการณ์ DST บางวันมี 23 หรือ 25 ชั่วโมงและวินาทีกระโดดยังมีอยู่
Peteris

คำตอบ:


23

การจัดเก็บข้อมูลในคอลัมน์เดียวเป็นวิธีที่ต้องการเนื่องจากมีการเชื่อมโยงอย่างแยกไม่ออก จุดในเวลานั้นเป็นข้อมูลชิ้นเดียวไม่ใช่สองชิ้น

วิธีการทั่วไปในการจัดเก็บข้อมูลวันที่ / เวลาที่ใช้ "เบื้องหลัง" โดยผลิตภัณฑ์จำนวนมากคือการแปลงเป็นค่าทศนิยมโดยที่ "วันที่" เป็นส่วนจำนวนเต็มของค่าทศนิยมและ "เวลา" เป็นเศษส่วน ราคา. ดังนั้น 1900-01-01 00:00:00 จะถูกเก็บไว้ที่ 0.0 และ 20 กันยายน 2016 9:34:00 ถูกเก็บไว้เป็น 42631.39861 42631 คือจำนวนวันตั้งแต่ 1900-01-01 .39861 เป็นส่วนของเวลาที่ผ่านไปตั้งแต่เที่ยงคืน อย่าใช้ประเภททศนิยมโดยตรงเพื่อทำสิ่งนี้ใช้ประเภทวันที่ / เวลาที่ชัดเจน จุดของฉันที่นี่เป็นเพียงภาพประกอบ

การจัดเก็บข้อมูลในสองคอลัมน์แยกกันหมายความว่าคุณจะต้องรวมค่าคอลัมน์ทั้งสองเมื่อใดก็ตามที่คุณต้องการดูว่าจุดที่กำหนดในเวลานั้นเร็วหรือช้ากว่าค่าที่เก็บไว้หรือไม่

หากคุณเก็บค่าไว้ต่างหากคุณจะพบกับ "บั๊ก" ที่ตรวจจับได้ยาก ยกตัวอย่างเช่น

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

ในโค้ดข้างต้นเรากำลังสร้างตารางทดสอบเติมข้อมูลด้วยค่าสองค่าจากนั้นทำการสืบค้นแบบง่าย ๆ กับข้อมูลนั้น ครั้งแรกที่SELECTส่งกลับทั้งสองแถวอย่างไรก็ตามที่สองSELECTส่งกลับเพียงแถวเดียวซึ่งอาจไม่ได้ผลลัพธ์ที่ต้องการ:

ป้อนคำอธิบายรูปภาพที่นี่

วิธีที่ถูกต้องในการกรองช่วงวันที่ / เวลาที่ค่าอยู่ในคอลัมน์ที่ไม่ต่อเนื่องตามที่ระบุโดย @ypercube ในความคิดเห็นคือ:

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

หากคุณต้องการแยกองค์ประกอบเวลาเพื่อการวิเคราะห์คุณสามารถพิจารณาเพิ่มคอลัมน์จากการคำนวณยังคงอยู่สำหรับส่วนเวลาของค่า:

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

SELECT *
FROM #dt;

ป้อนคำอธิบายรูปภาพที่นี่

คอลัมน์ที่คงอยู่นั้นจะสามารถจัดทำดัชนีอนุญาตให้เรียงลำดับอย่างรวดเร็ว ฯลฯ ตามเวลาของวัน

หากคุณกำลังพิจารณาแยกวันที่และเวลาออกเป็นสองฟิลด์เพื่อจุดประสงค์ในการแสดงผลคุณควรตระหนักว่าการจัดรูปแบบควรทำที่ไคลเอ็นต์ไม่ใช่เซิร์ฟเวอร์


11

ฉันจะให้ความเห็นแย้งกับคำตอบอื่น ๆ

หากจำเป็นต้องใช้ทั้งวันที่และเวลาร่วมกันเช่นรายการไม่ถูกต้องถ้ามันมีองค์ประกอบหนึ่ง แต่ไม่ใช่องค์ประกอบอื่น (หรือเป็น NULL ในหนึ่ง แต่ไม่ใช่องค์ประกอบอื่น) จากนั้นจัดเก็บไว้ในคอลัมน์เดียวทำให้รู้สึกถึงเหตุผลที่กำหนด คำตอบ

อย่างไรก็ตามอาจเป็นกรณีที่ส่วนประกอบอย่างใดอย่างหนึ่งหรือทั้งสองอย่างเป็นตัวเลือกแยกกัน ในกรณีนั้นมันจะไม่ถูกต้องในการจัดเก็บไว้ในคอลัมน์เดียว การทำเช่นนั้นจะบังคับให้คุณแสดงค่า NULL ในลักษณะที่ไม่เจาะจงเช่นจัดเก็บเวลาเป็น 00:00:00

นี่คือตัวอย่างบางส่วน:

  • คุณกำลังบันทึกการเดินทางของยานพาหนะสำหรับการหักภาษีไมล์สะสม การรู้เวลาที่แน่นอนในการเดินทางจะเป็นประโยชน์ แต่ถ้าพนักงานไม่ได้จดไว้และลืมไปแล้ววันที่ก็ควรบันทึกด้วยตัวเอง (วันที่ต้องการเวลาเลือก)

  • คุณกำลังทำแบบสำรวจเพื่อค้นหาเวลาที่ผู้คนกินอาหารกลางวันของพวกเขาและคุณขอให้ผู้เข้าร่วมกรอกแบบฟอร์มด้วยเวลาตัวอย่างอาหารกลางวันของพวกเขารวมถึงวันที่ บางคนไม่ต้องการกรอกข้อมูลในวันที่และคุณไม่ต้องการทิ้งข้อมูลเนื่องจากเป็นเวลาที่คุณสนใจจริงๆ (วันที่เลือกได้เวลาที่ต้องการ)

ดูคำถามที่เกี่ยวข้องนี้สำหรับแนวทางอื่น


ในRFC 3339มีข้อตกลงสำหรับการบันทึก "ไม่ทราบชื่อท้องถิ่นชดเชย" ฉันไม่คิดว่ามันค่อนข้างครอบคลุมกรณีการใช้งานของ "เวลาที่ไม่รู้จัก" แต่มันใกล้เคียง ส่วนถัดไป "เวลาท้องถิ่นที่ไม่มีเงื่อนไข" ยิ่งใกล้เข้ามาอีกครั้ง แต่ก็ไม่เพียงพอ
genorama

ใช่ฉันกำลังจ้องมองบาร์เรลเพื่อปรับโครงสร้างของฉันอีกครั้งเพราะตอนนี้ ใช้สถานการณ์การเช่ารถ ในการรับรถยนต์จาก บริษัท ที่ให้เช่า บริษัท จะต้องเปิด เพื่อให้คุณระบุวันที่และเวลาสำหรับรถกระบะ อย่างไรก็ตามหลายคนมีกล่องใส่กุญแจ เพื่อให้คุณส่งคืนหลังจากชั่วโมง ดังนั้นหากปิดทำการในวันอาทิตย์ มีวันที่ส่ง แต่ไม่ใช่เวลา การจัดเก็บค่า 0 (เช่น 12.00 น.) จะไม่ทำงานเนื่องจากบางสถานที่เปิดจนถึงเที่ยงคืนซึ่งเป็นค่าที่ถูกต้องในสถานการณ์อื่น ๆ
Reece

5

ฉันจะเก็บไว้เป็นคอลัมน์เดียวเสมอเว้นแต่จะมีความต้องการเฉพาะธุรกิจ / แอปพลิเคชัน ข้างล่างนี้คือคะแนนของฉัน -

  • การแตกเวลาจากการประทับเวลาไม่ใช่ปัญหา
  • เหตุใดจึงต้องเพิ่มคอลัมน์พิเศษเผื่อเวลาหากเราสามารถจัดเก็บทั้งสองไว้ด้วยกันได้
  • เพื่อหลีกเลี่ยงการเพิ่มวันที่และเวลาในแต่ละครั้งที่คุณทำการสอบถาม

1
@a_horse_with_no_name มีจุดที่นี่ ผมคิดว่า"แยกจากการประทับเวลา datetimestamp ไม่เป็นปัญหา"ควรจะซักค้านเป็น"การดึงเวลาจากการประทับเวลาไม่ได้เป็นปัญหา" "Timestamp" มักจะหมายถึงทั้งวันที่และเวลา (และตามปกติคือเขตเวลา)
ypercubeᵀᴹ

ใช่เห็นด้วย @ ypercubeᵀᴹ การประทับเวลามักจะหมายถึงทั้งวันที่และเวลา ฉันพูดถึงคำ DateTimeStamp อย่างชัดเจนเพื่อให้ทุกคนสามารถเข้าใจว่าเรากำลังพูดถึงวันที่และเวลาทั้งคู่ แต่คุณก็ถูกต้อง แก้ไขคำตอบ
Ashwini Mohan

3

ใน SQL Server จะเป็นการดีที่สุดที่จะเก็บ DataTime เป็นหนึ่งเขตข้อมูล หากคุณสร้างดัชนีในคอลัมน์ DataTime สามารถใช้เป็นการค้นหาวันที่และค้นหาวันที่ได้ ดังนั้นหากคุณต้องการ จำกัด ระเบียนทั้งหมดที่มีอยู่สำหรับวันที่ระบุคุณยังสามารถใช้ดัชนีโดยไม่ต้องทำอะไรเป็นพิเศษ หากคุณต้องการสืบค้นส่วนเวลาคุณจะไม่สามารถใช้ดัชนีเดียวกันได้ดังนั้นหากคุณมีกรณีธุรกิจที่คุณสนใจเวลาของวันมากกว่า DateTime คุณควรจัดเก็บแยกต่างหากเพราะคุณจะต้องสร้าง ดัชนีในนั้นและปรับปรุงประสิทธิภาพ


1

แน่นอนว่าน่าเสียดายที่ไม่มีประเภท cross-DBMS มาตรฐานสำหรับสิ่งนี้ (เช่น INT และ VARCHAR สำหรับจำนวนเต็มและค่าสตริง) วิธีการข้ามฐานข้อมูลทั้งสองที่ฉันพบมานั้นใช้คอลัมน์ VARCHAR / CHAR เพื่อจัดเก็บค่า DataTime เป็นสตริงที่จัดรูปแบบตามมาตรฐาน ISO 8601 (สะดวกกว่าอ่านง่ายกว่ามนุษย์) และใช้ BIGINT เพื่อเก็บไว้เป็น POSIX timestamps (เก็บเพิ่มเติม มีประสิทธิภาพเร็วขึ้นและง่ายต่อการจัดการทางคณิตศาสตร์)


2
ใช่นั่นคือนั่นคือtimestampสิ่งที่มาตรฐาน SQL กำหนดไว้ การจัดเก็บเวลาประทับเป็นสตริงเป็นคำแนะนำที่แย่มาก
a_horse_with_no_name

0

หลังจากอ่านสิ่งต่างๆมากมายเวลา UTC Unix ใน BIGINT น่าจะเป็นทางออกที่ดีที่สุด TZDB timesone ID ใน VARCHAR สำหรับการจัดเก็บโซนเวลาหากจำเป็น ข้อโต้แย้งเล็กน้อย:

  1. TIMESTAMP และ DATETIME ทำการแปลงแบบ gimmicky ในพื้นหลังที่ดูเหมือนจะซับซ้อนและไม่ชัดเจน เซิร์ฟเวอร์เปลี่ยนจากเวลาท้องถิ่นเป็น UTC หรือเป็นเวลาและย้อนกลับเซิร์ฟเวอร์บางครั้งหรือไม่ พวงของค่าใช้จ่ายที่ซ่อนอยู่สำหรับทุกฟังก์ชั่น

  2. BIGINT (8KB) เป็นอย่างน้อยเป็นแสงหรือเบากว่าทศนิยมที่จำเป็นสำหรับการจัดเก็บข้อมูลในรูปแบบ xxxxxx.xxxxxx ซึ่งเป็นที่เก็บไว้ในทางปฏิบัติเป็นสอง INTs + บางสิ่งบางอย่างโดย MySQL และก็เพียงพอที่จะเก็บไว้ล่วงหน้าหลายศตวรรษ

  3. ภาษาการเขียนโปรแกรมหลัก ๆ ส่วนใหญ่มีไลบรารีของฟังก์ชั่นมาตรฐานเพื่อใช้งานกับเวลา Unix

  4. การดำเนินการทางคณิตศาสตร์กับ BIGINT น่าจะเร็วหรือเร็วกว่าสิ่งอื่นใดบนฮาร์ดแวร์ใด ๆ

แน่นอนทั้งหมดข้างต้นเกี่ยวข้องกับโครงการขนาดใหญ่ระดับสากล สำหรับสิ่งเล็ก ๆ การใช้รูปแบบเริ่มต้นของเฟรมเวิร์กที่เลือกนั้นน่าจะดีพอ


2
" ทำการแปลงแบบ gimmicky ในพื้นหลังที่ดูเหมือนว่า ... ไม่ชัดเจน " - คุณกำลังพูดถึง DBMS ใดอยู่ สำหรับtimestampคอลัมน์ที่ไม่มี "การแปลงลูกเล่น" เกิดขึ้น (ที่เลเยอร์ฐานข้อมูล) และtimestamp with time zoneมีการจัดทำเอกสารอย่างดีและอธิบายไว้ในคู่มือ (อย่างน้อยสำหรับ Oracle และ Postgres)
a_horse_with_no_name

1
"ภาษาการเขียนโปรแกรมที่สำคัญเกือบทั้งหมดมีไลบรารีของฟังก์ชั่นมาตรฐานเพื่อใช้งานกับเวลา Unix" และคุณยังได้ทิ้งไลบรารีและฟังก์ชั่นทั้งหมดเกี่ยวกับวันที่ datetimes และ timestamps ที่ SQL / DBMS มีด้วยตัวเลือกในการใช้ bigint ...
ypercubeᵀᴹ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.