ฉันจะตัดทอนข้อมูลวันที่ใน SQL Server ได้อย่างไร


281

เป็นวิธีที่ดีที่สุดในการตัดทอนค่าวันที่และเวลา (เป็นลบชั่วโมงนาทีและวินาที) ใน SQL Server 2008 อะไร

ตัวอย่างเช่น:

declare @SomeDate datetime = '2009-05-28 16:30:22'
select trunc_date(@SomeDate)

-----------------------
2009-05-28 00:00:00.000

คำตอบ:


495

สิ่งนี้ยังคงรวบรวมคะแนนเสียงเพิ่มเติมบ่อยครั้งแม้กระทั่งหลายปีต่อมาและดังนั้นฉันจำเป็นต้องอัปเดตสำหรับ SQL Server รุ่นที่ทันสมัย สำหรับ SQL Server 2008 และใหม่กว่านั้นง่ายมาก:

cast(getDate() As Date)

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

แต่ก็มีวิธีอื่นที่จะทำให้สำเร็จเช่นกัน นี่เป็นเรื่องธรรมดาที่สุด

วิธีที่ถูกต้อง (ใหม่ตั้งแต่ SQL Server 2008):

cast(getdate() As Date)

วิธีที่ถูกต้อง (เก่า):

dateadd(dd, datediff(dd,0, getDate()), 0)

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

วิธีที่ถูกต้องนี้ใช้ฟังก์ชั่นที่บันทึกไว้ซึ่งเป็นส่วนหนึ่งของมาตรฐาน ansi และรับประกันว่าจะทำงานได้ แต่อาจช้าลงบ้าง มันทำงานได้โดยการค้นหาว่ามีกี่วันจากวันที่ 0 ถึงวันที่ปัจจุบันและเพิ่มว่าหลายวันกลับไปวันที่ 0 มันจะทำงานไม่ว่าวิธีการเก็บข้อมูลวันที่ของคุณและไม่ว่าสถานที่ของคุณคืออะไร

วิธีที่รวดเร็ว:

cast(floor(cast(getdate() as float)) as datetime)

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

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

ทางที่ผิด:

cast(convert(char(11), getdate(), 113) as datetime)

วิธีที่ไม่ถูกต้องทำงานโดยการแปลงสตริงตัดทอนสตริงและแปลงกลับเป็นวันที่และเวลา มันผิดด้วยเหตุผลสองประการ: 1) มันอาจไม่ทำงานในทุก ๆ ที่และ 2) มันเกี่ยวกับวิธีที่ช้าที่สุดในการทำเช่นนี้ ... และไม่ใช่เพียงเล็กน้อย มันเหมือนกับลำดับความสำคัญหรือช้ากว่าตัวเลือกอื่นสองตัว


อัปเดตสิ่งนี้ได้รับการลงคะแนนเมื่อเร็ว ๆ นี้และฉันต้องการเพิ่มลงไปเนื่องจากฉันโพสต์สิ่งนี้ฉันได้เห็นหลักฐานที่ชัดเจนว่าเซิร์ฟเวอร์ SQL จะปรับความแตกต่างของประสิทธิภาพระหว่างวิธี "ถูกต้อง" และ "เร็ว" ซึ่งหมายความว่าคุณควรให้ความโปรดปรานแก่อดีต

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

ในสถานที่ส่วนใหญ่ฐานข้อมูลเป็นคอขวดของคุณแล้ว โดยทั่วไปแล้วเซิร์ฟเวอร์ที่แพงที่สุดในการเพิ่มฮาร์ดแวร์ให้กับการปรับปรุงประสิทธิภาพและสิ่งที่ยากที่สุดในการเพิ่มส่วนประกอบเหล่านั้นให้ถูกต้อง (คุณต้องสร้างสมดุลของดิสก์กับหน่วยความจำเป็นต้น) นอกจากนี้ยังเป็นการยากที่สุดในการขยายขอบเขตออกไปทั้งด้านเทคนิคและจากมุมมองทางธุรกิจ ในทางเทคนิคแล้วการเพิ่มเว็บหรือเซิร์ฟเวอร์แอปพลิเคชั่นนั้นง่ายกว่าการใช้เซิร์ฟเวอร์ฐานข้อมูลและแม้ว่ามันจะผิดพลาดก็ตามคุณไม่ต้องจ่าย $ 20,000 + ต่อสิทธิ์ใช้งานเซิร์ฟเวอร์สำหรับ IIS หรือ Apache

ประเด็นที่ฉันพยายามทำคือเมื่อเป็นไปได้คุณควรทำงานนี้ในระดับแอปพลิเคชัน เฉพาะเวลาที่คุณเคยควรพบว่าตัวเองตัดทอน datetime ในโปรแกรม Sql Server คือเมื่อคุณจำเป็นต้องจัดกลุ่มตามวันและแม้แล้วคุณอาจจะมีคอลัมน์พิเศษตั้งค่าเป็นคอลัมน์ที่คำนวณไว้ในเวลาแทรก / ปรับปรุงหรือการบำรุงรักษา ในแอปพลิเคชันตรรกะ รับดัชนีนี้ทำลายซีพียูหนักจากฐานข้อมูลของคุณ


6
"วิธีที่เร็ว" ยังคงเป็นวิธีที่เร็วที่สุดสำหรับ sql 2008 ตามมาตรฐานที่ฉันเพิ่งวิ่งไป
Sam Saffron

3
FYI: stackoverflow.com/q/1177449/27535และstackoverflow.com/q/133081/27535 dateadd / dateiff "ชนะ ... " สำหรับตัวแปรตัวเดียวที่ใส่ใจแน่นอนและใคร ๆ ก็หวังว่าคุณจะคำนวณคอลัมน์หรือมากกว่าหนึ่งล้านแถว :-)
gbn

9
วิธีนี้ "ถูกต้อง" ใช้งานได้โดยไม่ตั้งใจเท่านั้น วิธีการเขียนนั้นเหมือนกับว่าไวยากรณ์สำหรับ DateAdd คือ (ช่วงเวลาวันที่เพิ่มขึ้น) แต่ไม่ใช่ มันคือ (ช่วงเวลาเพิ่มวันที่) ฉันสะดุดเมื่อฉันพยายามตัดทอนวันที่เป็นวันแรกของเดือน: SELECT DATEADD (m, 0, DATEDIFF (m, 0, GETDATE ())) ไม่ทำงาน แต่ SELECT DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) ทำ อย่างน้อยนี่คือสิ่งที่ฉันเห็นในปี 2008R2
Kelly Cline

1
@ เคลลี่ในปี 2008R2 ทำไมไม่เป็นเช่นนั้นcast(getdate() as date)?
Joel Coehoorn

2
พวกเขาทั้งหมดทำงานในคอลัมน์วันที่และเวลา getdate()นี่คือการสแตนด์บายสำหรับแหล่งข้อมูลวันที่คุณอาจมี
Joel Coehoorn

44

สำหรับ SQL Server 2008 เท่านั้น

CAST(@SomeDateTime AS Date) 

จากนั้นส่งกลับไปที่ datetime ถ้าคุณต้องการ

CAST(CAST(@SomeDateTime AS Date) As datetime)

จุดที่ดี: ฉันยังคงอยู่ในปี 2005 และดังนั้นสำหรับปี 2008 นี่อาจเป็นวิธีที่ "ถูกต้อง" ใหม่และอาจตรงกับประสิทธิภาพของวิธี "เร็ว"
Joel Coehoorn

1
ประสิทธิภาพของวิธีการใหม่นี้จะเร็วกว่าวิธี "เร็ว"
ErikE

21

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

นี่คือความแตกต่างจากคำตอบที่ยอมรับซึ่งคุณสามารถใช้ไม่เพียงdd(วัน) แต่ส่วนใด ๆ ของวันที่ (ดูที่นี่ ):

dateadd(minute, datediff(minute, 0, GETDATE()), 0)

โปรดทราบว่าในนิพจน์ด้านบน0เป็นวันที่คงที่ในต้นปี (1900-01-01) หากคุณต้องการตัดส่วนที่มีขนาดเล็กลงเช่นวินาทีหรือมิลลิวินาทีคุณจะต้องใช้วันที่คงที่ซึ่งใกล้เคียงกับวันที่จึงจะถูกตัดทอนเพื่อหลีกเลี่ยงการล้น


1
นี่เป็นประโยชน์อย่างมาก ฉันมองหาวิธีตัดทอนวันเวลาในสถานที่ที่ต่ำกว่าวันเต็ม
Michael - ที่ไหน Shirky Clay

1
@Michael ขอบคุณสำหรับความคิดเห็นดีที่รู้ว่ามันช่วยคุณออก!
Lucero

1
+1 สิ่งนี้ควรมี upvotes มากกว่านี้เป็นคำตอบที่ดีที่ขยายออกไปสำหรับคำตอบที่เลือก
jtate

1
เพื่อให้อินเทอร์เน็ตรู้ว่าคุณไม่จำเป็นต้อง จำกัด ช่วงวันที่เต็ม นี่คือตัวอย่างสำหรับช่วงเวลา 15 นาทีโดยใช้การแบ่งจำนวนเต็ม:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael - Where's Clay Shirky

7

ตัวอย่างที่ฉันพบบนเว็บเมื่อฉันต้องทำคือ:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))

ฉันอยู่ปี 2005 แต่ฉันคิดว่าปี 2008 มีฟังก์ชั่นใหม่สำหรับเรื่องนี้ ??
กม.

2
เรียบร้อย! ฉันจะใช้วิธีแยกส่วนวันที่ออกและใช้การจัดการสตริงเพื่อนำกลับมารวมกัน อาจไม่เกี่ยวข้อง แต่ SQL2008 มีประเภทข้อมูลเฉพาะวันที่โดยไม่มีอิลิเมนต์เวลา
ฟรานส์

1
และโปรดทราบว่าคุณมีตัวถูกดำเนินการ DateAdd รวมอยู่DateAdd(dd, DateDiff(...), 0)ด้วย สิ่งนี้อาจกัดคุณได้หากคุณไม่ระวัง
ErikE

1

ใน SQl 2005 ฟังก์ชัน trunc_date ของคุณสามารถเขียนได้เช่นนี้

(1)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
    CAST(FLOOR( CAST( @date AS FLOAT ) )AS DATETIME)
END

วิธีแรกนั้นสะอาดกว่ามาก ใช้การเรียกเมธอดเพียง 3 ครั้งเท่านั้นรวมถึง CAST สุดท้าย () และไม่มีการต่อสตริงซึ่งเป็นการบวกอัตโนมัติ ยิ่งกว่านั้นไม่มีการฉายขนาดใหญ่ที่นี่ หากคุณสามารถจินตนาการได้ว่าสามารถแสดงวันที่ / เวลาได้การแปลงจากวันที่เป็นตัวเลขและกลับเป็นวันที่เป็นกระบวนการที่ค่อนข้างง่าย

(2)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
      SELECT CONVERT(varchar, @date,112)
END

หากคุณกังวลเกี่ยวกับการใช้ชุดข้อมูล (2) หรือ (3) ของไมโครซอฟท์อาจไม่เป็นไร

(3)

CREATE FUNCTION trunc_date(@date DATETIME)
RETURNS DATETIME
AS
BEGIN
SELECT CAST((STR( YEAR( @date ) ) + '/' +STR( MONTH( @date ) ) + '/' +STR( DAY(@date ) )
) AS DATETIME
END

ประการที่สามวิธีการ verbose มากขึ้น สิ่งนี้ต้องแบ่งวันที่ออกเป็นส่วนปีเดือนและวันรวมกันในรูปแบบ "yyyy / mm / dd" จากนั้นส่งกลับไปที่วันที่ วิธีนี้เกี่ยวข้องกับการเรียกใช้เมธอด 7 ครั้งรวมถึง CAST สุดท้าย () ไม่ต้องพูดถึงการต่อสตริง




0

สำหรับบรรดาของคุณที่มาที่นี่กำลังมองหาวิธีตัดทอนเขตข้อมูล DATETIME ให้เป็นบางสิ่งที่น้อยกว่าทั้งวันตัวอย่างเช่นทุกนาทีคุณสามารถใช้สิ่งนี้:

SELECT CAST(FLOOR(CAST(GETDATE() AS FLOAT)) + (FLOOR((CAST(GETDATE() AS FLOAT) - FLOOR(CAST(GETDATE() AS FLOAT))) * 1440.0) + (3.0/86400000.0)) / 1440.0 AS DATETIME)

ดังนั้นหากวันนี้เป็นวัน2010-11-26 14:54:43.123นี้ก็จะกลับมา2010-11-26 14:54:00.000แล้วนี้จะกลับมา

ในการเปลี่ยนช่วงเวลาที่เปลี่ยนไปเป็น 1440.0 ให้แทนที่ด้วยจำนวนช่วงเวลาในหนึ่งวันเช่น

24hrs          =   24.0  (for every hour)
24hrs / 0.5hrs =   48.0  (for every half hour)
24hrs / (1/60) = 1440.0  (for every minute)

(วาง.0ท้ายเสมอเพื่อโยนทิ้งโดยปริยาย)


สำหรับคนที่คุณสงสัยว่าการ(3.0/86400000)คำนวณของฉันคืออะไร SQL Server 2005 ดูเหมือนจะไม่ถูกFLOATนำไปใช้DATETIMEอย่างแม่นยำดังนั้นจึงเพิ่ม 3 มิลลิวินาทีก่อนที่จะปูพื้น


1
ระวังข้อผิดพลาดในการปัดเศษเนื่องจากข้อ จำกัด ด้านความแม่นยำของจุดลอยตัว ... แต่ก็ไม่สามารถใช้ได้กับdatetime2ชนิดข้อมูล
Lucero

สำหรับชั่วโมง SELECT DATEADD (ชั่วโมง DATEDIFF (ชั่วโมง, 0, GETDATE ()), 0) ก็ใช้งานได้เช่นกัน นาทีเช่นกัน แต่ข้อที่สองจะส่งผลให้เกิดการล้น
Kelly Cline

หล่อลอยและกลับไปยังวันที่และเวลาทำงานไม่ถูกต้อง
ErikE

0

แบบสอบถามนี้ควรให้ผลลัพธ์เทียบเท่ากับtrunc(sysdate)ใน Oracle

SELECT  * 
FROM    your_table
WHERE   CONVERT(varchar(12), your_column_name, 101)
      = CONVERT(varchar(12), GETDATE(), 101)

หวังว่านี่จะช่วยได้!


0

นอกจากนี้คุณยังสามารถแยกวันที่using Substringจากตัวแปรวันที่และการส่งกลับไปยังวันที่และเวลาจะไม่สนใจเวลาส่วนหนึ่ง

declare @SomeDate datetime = '2009-05-28 16:30:22'
SELECT cast(substring(convert(varchar(12),@SomeDate,111),0,12) as Datetime) 

นอกจากนี้คุณสามารถเข้าถึงบางส่วนของตัวแปร datetime และรวมเข้ากับวันที่สร้างแบบตัดทอนบางสิ่งเช่นนี้:

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)

0

ออราเคิล:

TRUNC(SYSDATE, 'MONTH')

เซิร์ฟเวอร์ SQL:

DATEADD(DAY, - DATEPART(DAY, DateField) + 1, DateField)

สามารถใช้ในการตัดทอนนาทีหรือชั่วโมงจากวันที่ในทำนองเดียวกัน



โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.