Konversi dari tipe data varchar ke tipe data datetime menghasilkan nilai out-of-range

8

Saya mencoba menjalankan kueri sederhana untuk membuat semua baris dibuat pada bulan November:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN '2014-11-01 00:00:00.000' 
AND '2014-11-30 23:59:59.997';

Pengembalian SMS:

Konversi tipe data varchar ke tipe data datetime menghasilkan nilai di luar kisaran.

Saya tidak mengerti mengapa data sedang dikonversi dari varchar ke datetime ketika 'Dibuat' diatur ke datetime:

Kolom Apakah saya perlu memberi tahu server bahwa 'Dibuat' adalah datetime? Jika tidak, mengapa saya menerima pesan varchar ini?

Sunting: Nilai dalam database adalah YYYY-MM-DD. Balas dari @SqlZim di bawah ini mengatakan bahwa saya perlu menggunakan convert () untuk memberi tahu sql format tanggal apa di db - dan untuk mengganti karakter spasi dengan huruf T:

select count(*) 
from dbo.profile 
where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
and convert(datetime,'2014-11-30T23:59:59.997');`
jedluddley
sumber

Jawaban:

8

Saya memeriksa profil Anda dan melihat bahwa Anda berada di Inggris. Jika server sql Anda diatur untuk menggunakan dmy format tanggal maka itu menjelaskan masalah Anda. Tanpa menggunakan 'T' alih-alih ruang dalam string datetime, Sql Server tidak akan mengenalinya sebagai format ISO8601.

Coba ini:

select count(*) 
  from dbo.profile 
  where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
                      and convert(datetime,'2014-11-30T23:59:59.997');

Permintaan menggunakan tanggal dan / atau data bisa rumit, untuk memastikan Anda mendapatkan apa yang Anda cari, saya sarankan membaca:

sunting: untuk memperjelas nilai di luar rentang dalam pesan kesalahan Anda akan dari menafsirkan bulan sebagai 30 dan hari sebagai 11.

SqlZim
sumber
8

Saya tidak mengerti mengapa data sedang dikonversi dari varchar ke datetime ketika 'Dibuat' diatur ke datetime

Literal yang Anda sediakan untuk perbandingan dengan Createdkolom adalah string. Untuk membandingkan literal tersebut dengan datetimekolom, SQL Server mencoba untuk mengubah string ke datetimetipe, sesuai dengan aturan prioritas tipe data . Tanpa informasi eksplisit tentang format string, SQL Server mengikuti aturan yang berbelit - belit untuk menafsirkan string sebagai datetimes.

Dalam pandangan saya, cara paling rapi untuk menghindari masalah-masalah ini adalah secara eksplisit tentang jenis-jenis. SQL Server menyediakan CAST and CONVERTfungsi untuk tujuan ini. Ketika bekerja dengan string dan tipe tanggal / waktu, CONVERTlebih disukai karena menyediakan parameter gaya untuk secara eksplisit menentukan format string.

Pertanyaannya menggunakan string dalam format ODBC kanonik (dengan milidetik) (gaya 121). Menjadi eksplisit tentang tipe data dan hasil gaya string sebagai berikut:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    CONVERT(datetime, '2014-11-01 00:00:00.000', 121)
    AND 
    CONVERT(datetime, '2014-11-30 23:59:59.997', 121);

Yang mengatakan, ada alasan bagus (seperti yang ditunjukkan Aaron dalam jawabannya ) untuk menggunakan rentang setengah terbuka alih-alih BETWEEN(saya menggunakan gaya 120 di bawah hanya untuk variasi):

SELECT COUNT(*)
FROM dbo.profile 
WHERE
    [Created] >= CONVERT(datetime, '2014-11-01 00:00:00', 120)
    AND [Created] < CONVERT(datetime, '2014-12-01 00:00:00', 120);

Menjadi eksplisit tentang jenis adalah kebiasaan yang sangat baik untuk dilakukan, terutama ketika berhadapan dengan tanggal dan waktu.

Paul White 9
sumber
3

Alternatif lain, saya sarankan menggunakan ODBC literal literal . Meskipun namanya mereka tidak mengharuskan Anda terhubung melalui ODBC. Mereka memintas aturan konversi yang biasa dalam SQL Server dan selalu ditafsirkan sebagai datetime.

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    {TS '2014-11-01 00:00:00.000'}
    AND 
    {TS '2014-11-30 23:59:59.997'};


datetimeLiteral ODBC lain yang didukung adalah Ddan Tsebagaimana didokumentasikan di sini di Books Online. Keduanya kembali datetime(tidak dateatau time), tetapi sintaksisnya masih kompak dan tidak ambigu. Format tetap untuk string adalah:

Format string ODBC

Contoh:

SELECT TOP (1)
    D = {D '2014-12-27'},
    T = {T '14:49:23.789'},
    TS = {TS '2014-12-27 14:49:23.789'};

The Tvarian mengembalikan waktu yang ditentukan pada hari ini , seperti dilansir internal digunakan hanya {fn getdateODBC()}:

Rencana eksekusi

Michael B
sumber
1
Yah saya mungkin hanya akan lakukan CONVERT(DATE, '20141201')jika kebutuhan Anda untuk secara eksplisit mengalahkan semua yang lain. Kemudian lagi, jika kolom yang mendasarinya adalah tipe tanggal / waktu, ini tidak benar-benar diperlukan. Apakah Anda mengatakan WHERE Active = CONVERT(BIT, 1)untuk menghindari WHERE Active = 1ditafsirkan sebagai INT?
Aaron Bertrand
3
@ AaronBertrand Sebenarnya, saya diketahui melakukan hal itu :) Dan inilah salah satu contohnya .
Paul White 9
-1

kode di bawah ini, dapatkan sesi saat ini dateformat, mendapat kesalahan saat mengonversi ke datetime, lalu atur format tanggal dan tahan ymd uji konversi yang terakhir dan bekerja lagi dan itu berfungsi

-- set the dateformat for the current session
-- if you use this date format you get the following error message:
--Msg 242, Level 16, State 3, Line 9
--The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
set dateformat dmy


-- set the dateformat for the current session
--this one does not give an error message
set dateformat ymd

-- The conversion of a varchar data type 
-- to a datetime data type resulted in an out-of-range value.
select cast('2017-08-13 16:31:31'  as datetime)

-- get the current session date_format
select date_format
from sys.dm_exec_sessions
where session_id = @@spid

-- set the dateformat for the current session
set dateformat ymd

-- this should work
select cast('2017-08-13 16:31:31'  as datetime)



select @@version

Microsoft SQL Server 2016 (SP1) (KB3182545) - 13.0.4001.0 (X64) 28 Okt 2016 18:17:30 Hak cipta (c) Microsoft Corporation Enterprise Edition: Lisensi Berbasis Core (64-bit) pada Windows Server 2012 R2 Datacenter 6.3 (Bangun 9600:) (Hypervisor)

Marcello Miorelli
sumber
peduli jelaskan suara turun ?, kodenya bekerja dengan baik di sini!
Marcello Miorelli