Bagaimana saya bisa memotong datetime di SQL Server?

281

Apa cara terbaik untuk memotong nilai datetime (untuk menghapus jam menit dan detik) di SQL Server 2008?

Sebagai contoh:

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

-----------------------
2009-05-28 00:00:00.000
Julio César
sumber

Jawaban:

495

Ini terus sering mengumpulkan suara tambahan, bahkan beberapa tahun kemudian, jadi saya perlu memperbaruinya untuk versi modern Sql Server. Untuk Sql Server 2008 dan yang lebih baru, sangat sederhana:

cast(getDate() As Date)

Perhatikan bahwa tiga paragraf terakhir di dekat bagian bawah masih berlaku, dan Anda sering harus mengambil langkah mundur dan menemukan cara untuk menghindari para pemain di tempat pertama.

Tetapi ada cara lain untuk mencapai ini juga. Inilah yang paling umum.

Cara yang benar (baru sejak Sql Server 2008):

cast(getdate() As Date)

Cara yang benar (lama):

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

Ini sudah lebih tua sekarang, tetapi masih perlu diketahui karena ia juga dapat dengan mudah beradaptasi untuk titik waktu lainnya, seperti momen pertama bulan, menit, jam, atau tahun.

Cara yang benar ini menggunakan fungsi-fungsi yang didokumentasikan yang merupakan bagian dari standar ansi dan dijamin berfungsi, tetapi bisa jadi agak lambat. Ia bekerja dengan menemukan berapa hari ada dari hari 0 hingga hari ini, dan menambahkan bahwa banyak hari kembali ke hari 0. Ini akan bekerja tidak peduli berapa lama penyimpanan data Anda dan tidak peduli apa lokal Anda.

Cara cepat:

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

Ini berfungsi karena kolom datetime disimpan sebagai nilai biner 8-byte. Buang mereka untuk mengapung, lantai mereka untuk menghapus fraksi, dan bagian waktu dari nilai-nilai hilang ketika Anda melemparkannya kembali ke datetime. Itu semua hanya sedikit bergeser tanpa logika yang rumit dan sangat cepat.

Ketahuilah bahwa ini bergantung pada detail implementasi yang dapat diubah oleh Microsoft kapan saja, bahkan dalam pembaruan layanan otomatis. Ini juga tidak terlalu portabel. Dalam praktiknya, sangat tidak mungkin implementasi ini akan berubah dalam waktu dekat, tetapi penting untuk mewaspadai bahaya jika Anda memilih untuk menggunakannya. Dan sekarang kita memiliki opsi untuk berperan sebagai kencan, jarang diperlukan.

Jalan yang salah:

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

Cara yang salah berfungsi dengan mengonversi string, memotong string, dan mengonversi kembali ke datetime. Itu salah , karena dua alasan: 1) mungkin tidak bekerja di semua lokal dan 2) ini tentang cara paling lambat untuk melakukan ini ... dan bukan hanya sedikit; itu seperti urutan besarnya atau dua lebih lambat dari opsi lain.


Pembaruan Ini telah mendapatkan beberapa suara akhir-akhir ini, jadi saya ingin menambahkan bahwa sejak saya memposting ini saya telah melihat beberapa bukti yang cukup kuat bahwa Sql Server akan mengoptimalkan perbedaan kinerja antara cara "benar" dan cara "cepat" , berarti Anda sekarang harus mendukung yang pertama.

Dalam kedua kasus tersebut, Anda ingin menulis kueri Anda untuk menghindari keharusan melakukan ini sejak awal . Sangat jarang Anda harus melakukan ini pada database.

Di sebagian besar tempat, basis data sudah menjadi hambatan Anda. Biasanya server yang paling mahal untuk menambahkan perangkat keras untuk peningkatan kinerja dan yang paling sulit untuk mendapatkan penambahan itu dengan benar (misalnya, Anda harus menyeimbangkan disk dengan memori). Ini juga yang paling sulit untuk diukur keluar, baik secara teknis maupun dari sudut pandang bisnis; jauh lebih mudah secara teknis untuk menambahkan server web atau aplikasi daripada server database dan bahkan jika itu salah Anda tidak membayar $ 20.000 + per lisensi server untuk IIS atau apache.

Poin yang saya coba sampaikan adalah bahwa sedapat mungkin Anda harus melakukan pekerjaan ini di tingkat aplikasi. Satu- satunya waktu Anda harus menemukan diri Anda memotong datetime pada Sql Server adalah ketika Anda perlu mengelompokkan hari, dan bahkan kemudian Anda mungkin harus memiliki kolom tambahan yang diatur sebagai kolom yang dihitung, dikelola pada waktu memasukkan / perbarui, atau dikelola dalam logika aplikasi. Dapatkan pemecahan indeks, cpu-heavy ini dari basis data Anda.

Joel Coehoorn
sumber
6
"jalan cepat" masih merupakan cara tercepat untuk sql 2008 menurut tolok ukur yang baru saya jalankan
Sam Saffron
3
FYI: stackoverflow.com/q/1177449/27535 dan stackoverflow.com/q/133081/27535 Dateadd / datediff "menang ...". Untuk satu variabel, siapa yang peduli tentu saja, dan orang berharap bahwa Anda telah menghitung kolom atau lebih dari satu juta baris :-)
gbn
9
Cara "benar" ini hanya berfungsi secara tidak sengaja. Cara ini ditulis adalah seolah-olah sintaks untuk DateAdd adalah (interval, tanggal, kenaikan), tetapi tidak. Itu adalah (interval, kenaikan, tanggal). Saya tersandung pada ini ketika saya mencoba untuk memotong tanggal ke bulan pertama: SELECT DATEADD (m, 0, DATEIFF (m, 0, GETDATE ())) tidak berfungsi, tetapi SELECT DATEADD (m, DATEDIFF (m, 0, GETDATE ()), 0) tidak. Setidaknya, ini yang saya lihat di 2008R2.
Kelly Cline
1
@Kelly di 2008R2, mengapa tidak adil cast(getdate() as date)?
Joel Coehoorn
2
Mereka semua bekerja di kolom datetime. getdate()di sini ada stand-in untuk sumber datetime apa pun yang mungkin Anda miliki.
Joel Coehoorn
44

Hanya untuk SQL Server 2008

CAST(@SomeDateTime AS Date) 

Kemudian masukkan kembali ke datetime jika Anda mau

CAST(CAST(@SomeDateTime AS Date) As datetime)
DJ.
sumber
Poin bagus: Saya masih menggunakan 2005 dan jadi untuk 2008 ini mungkin cara "benar" baru dan bahkan mungkin cocok dengan kinerja cara "cepat".
Joel Coehoorn
1
Kinerja cara baru ini bahkan lebih cepat daripada cara "cepat".
ErikE
21

Demi jawaban yang lebih lengkap, inilah cara bekerja untuk memotong ke salah satu bagian tanggal turun dan termasuk menit (ganti GETDATE()dengan tanggal untuk memotong).

Ini berbeda dari jawaban yang diterima karena Anda dapat menggunakan tidak hanya dd(hari), tetapi bagian tanggal mana pun (lihat di sini ):

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

Perhatikan bahwa dalam ekspresi di atas, 0ini adalah tanggal konstan pada awal tahun (1900-01-01). Jika Anda perlu memotong ke bagian yang lebih kecil, seperti detik atau milidetik, Anda perlu mengambil tanggal konstan yang lebih dekat dengan tanggal yang akan terpotong untuk menghindari luapan.

Lucero
sumber
1
Ini sangat membantu. Saya mencari-cari cara untuk memotong tanggal-waktu di tempat yang lebih rendah dari sehari penuh.
Michael - Di mana Clay Shirky
1
@Michael, terima kasih atas umpan baliknya, senang mengetahui bahwa itu membantu Anda!
Lucero
1
+1 ini seharusnya memiliki lebih banyak upvotes, ini adalah jawaban yang bagus yang memperluas jawaban yang dipilih.
jtate
1
Supaya internet tahu, Anda tidak perlu dibatasi untuk periode data penuh. Berikut adalah contoh untuk interval 15 menit, menggunakan divisi integer:dateadd(minute, datediff(minute, 0, GETDATE()) / 15 * 15, 0)
Michael - Where's Clay Shirky
7

Cuplikan yang saya temukan di web ketika saya harus melakukan ini adalah:

 dateadd(dd,0, datediff(dd,0, YOURDATE))
 e.g.
 dateadd(dd,0, datediff(dd,0, getDate()))
Tom Ritter
sumber
Saya pada 2005, tapi saya pikir 2008 memiliki beberapa fungsi baru untuk ini ??
KM.
2
Rapi! Saya akan terpaksa memisahkan bagian-bagian data dan menggunakan penanganan string untuk menyatukannya kembali. Mungkin tidak relevan, tetapi SQL2008 memiliki tipe data hanya tanggal murni tanpa elemen waktu.
Frans
1
Dan perhatikan Anda memiliki operand DateAdd digabungkan, itu DateAdd(dd, DateDiff(...), 0). Ini bisa menggigit Anda jika Anda tidak hati-hati.
ErikE
1

Pada SQl 2005, fungsi trunc_date Anda dapat ditulis seperti ini.

(1)

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

Metode pertama jauh lebih bersih. Ini hanya menggunakan 3 metode panggilan termasuk CAST akhir () dan tidak melakukan penggabungan string, yang merupakan plus otomatis. Selain itu, tidak ada gips tipe besar di sini. Jika Anda dapat membayangkan bahwa cap Tanggal / Waktu dapat diwakili, maka mengubah dari tanggal ke angka dan kembali ke tanggal adalah proses yang cukup mudah.

(2)

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

Jika Anda khawatir tentang implementasi datetimes (2) atau (3) oleh microsoft mungkin ok.

(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

Ketiga, metode yang lebih verbose. Ini membutuhkan pengelompokan tanggal ke bagian tahun, bulan, dan hari, menempatkannya bersama dalam format "yyyy / mm / dd", lalu memasukkannya kembali ke tanggal. Metode ini melibatkan 7 pemanggilan metode termasuk CAST akhir (), belum lagi string concatenation.

AlejandroR
sumber
1
CONVERT(DATE, <yourdatetime>) or CONVERT(DATE, GetDate()) or CONVERT(DATE, CURRENT_TIMESTAMP)
Dekan
sumber
0

Bagi Anda yang datang ke sini mencari cara untuk memotong bidang DATETIME menjadi kurang dari satu hari penuh, misalnya setiap menit, Anda dapat menggunakan ini:

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)

jadi jika hari ini 2010-11-26 14:54:43.123maka ini akan kembali2010-11-26 14:54:00.000 .

Untuk mengubah intervalnya, gantilah 1440.0 dengan jumlah interval dalam sehari, misalnya:

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

(Selalu letakkan .0di ujung untuk secara implisit dilemparkan ke pelampung.)


Bagi Anda bertanya-tanya apa (3.0/86400000)yang dalam perhitungan saya, SQL Server 2005 tampaknya tidak cor dari FLOATke DATETIMEakurat, jadi ini menambah 3 milidetik sebelum lantai itu.

BG100
sumber
1
Hati-hati dengan kesalahan pembulatan karena batas presisi titik mengambang ... juga, ini tidak bekerja dengan datetime2tipe data.
Lucero
Untuk Hour, SELECT DATEADD (hour, DATEDIFF (hour, 0, GETDATE ()), 0) juga berfungsi. Menit juga, tetapi Kedua akan menghasilkan luapan.
Kelly Cline
Casting untuk mengapung dan kembali ke datetime tidak bekerja dengan benar .
ErikE
0

Kueri ini akan memberi Anda hasil yang setara dengan trunc(sysdate)di Oracle.

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

Semoga ini membantu!

Sandeep Gaadhe
sumber
0

Anda juga dapat mengekstrak tanggal using Substringdari variabel datetime dan mengembalikan ke datetime akan mengabaikan bagian waktu.

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

Selain itu, Anda dapat mengakses bagian variabel datetime dan menggabungkannya ke tanggal terpotong konstruk, seperti ini:

SELECT cast(DATENAME(year, @Somedate) + '-' + 
       Convert(varchar(2),DATEPART(month, @Somedate)) + '-' +
       DATENAME(day, @Somedate) 
       as datetime)
Tidak ada yang hilang
sumber
0

Peramal:

TRUNC(SYSDATE, 'MONTH')

SQL Server:

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

Bisa juga digunakan untuk memotong menit atau jam dari tanggal.

Markus
sumber
0

Anda bisa melakukan ini (SQL 2008):

mendeklarasikan @SomeDate date = getdate ()

select @SomeDate

2009-05-28

Hagai Danenberg-Lerner
sumber