Chuyển đổi loại dữ liệu varchar thành loại dữ liệu datetime dẫn đến giá trị ngoài phạm vi


8

Tôi đang cố gắng chạy một truy vấn đơn giản để có được tất cả các hàng được tạo vào tháng 11:

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

SMSS trả về:

Việc chuyển đổi một kiểu dữ liệu varchar thành kiểu dữ liệu datetime dẫn đến một giá trị ngoài phạm vi.

Tôi không hiểu tại sao dữ liệu đang được chuyển đổi từ varchar sang datetime khi 'created' được đặt thành datetime:

Cột Tôi có cần nói với máy chủ rằng 'Đã tạo' là datetime không? Nếu không, tại sao tôi nhận được thông báo varchar này?

Chỉnh sửa: Giá trị trong cơ sở dữ liệu là YYYY-MM-DD. Trả lời từ @SqlZim bên dưới nói rằng tôi cần sử dụng convert () để cho sql biết định dạng ngày trong db - và để thay thế ký tự khoảng trắng bằng chữ 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');`

Câu trả lời:


8

Tôi đã kiểm tra hồ sơ của bạn và thấy rằng bạn đang ở Vương quốc Anh. Nếu máy chủ sql của bạn được thiết lập để sử dụng dform dateformat thì điều đó giải thích vấn đề của bạn. Không sử dụng 'T' thay vì khoảng trắng trong chuỗi datetime, Sql Server sẽ không nhận ra nó là định dạng ISO8601.

Thử cái này:

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

Truy vấn bằng cách sử dụng ngày và / hoặc datetimes có thể khó khăn, để đảm bảo bạn đang nhận được những gì bạn đang tìm kiếm, tôi khuyên bạn nên đọc:

chỉnh sửa: để làm rõ giá trị ngoài phạm vi trong thông báo lỗi của bạn sẽ là từ việc diễn giải tháng là 30 và ngày là 11.


8

Tôi không hiểu tại sao dữ liệu được chuyển đổi từ varchar sang datetime khi 'created' được đặt thành datetime

Các chữ bạn đang cung cấp để so sánh với Createdcột là các chuỗi. Để so sánh các chữ đó với datetimecột, SQL Server cố gắng chuyển đổi các chuỗi thành datetimecác loại, theo các quy tắc ưu tiên của kiểu dữ liệu . Không có thông tin rõ ràng về định dạng của các chuỗi, SQL Server tuân theo các quy tắc phức tạp của nó để diễn giải các chuỗi dưới dạng thời gian.

Theo quan điểm của tôi, cách gọn gàng nhất để tránh các loại vấn đề này là rõ ràng về các loại. SQL Server cung cấp các CAST and CONVERTchức năng cho mục đích này. Khi làm việc với các chuỗi và các loại ngày / giờ, CONVERTsẽ được ưu tiên hơn vì nó cung cấp một tham số kiểu để xác định rõ ràng định dạng chuỗi.

Câu hỏi sử dụng các chuỗi ở định dạng chính tắc ODBC (với mili giây) (kiểu 121). Rõ ràng về kiểu dữ liệu và kiểu chuỗi kết quả như sau:

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

Điều đó nói rằng, có những lý do chính đáng (như Aaron chỉ ra trong câu trả lời của anh ấy ) để sử dụng phạm vi nửa mở thay vì BETWEEN(tôi sử dụng kiểu 120 dưới đây chỉ để đa dạng):

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

Rõ ràng về các loại là một thói quen rất tốt để có được, đặc biệt là khi xử lý ngày và thời gian.



3

Một cách khác, tôi khuyên bạn nên sử dụng ODBC datetime bằng chữ . Mặc dù tên của họ, họ không yêu cầu bạn kết nối qua ODBC. Chúng bỏ qua các quy tắc chuyển đổi thông thường trong SQL Server và luôn được hiểu là a 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'};


Các datetimechữ ODBC được hỗ trợ khác là DTnhư được ghi lại ở đây trong Sách trực tuyến. Cả hai trả về datetime(không datehoặc time), nhưng cú pháp vẫn nhỏ gọn và không rõ ràng. Các định dạng cố định cho chuỗi là:

Định dạng chuỗi ODBC

Thí dụ:

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

Các Tbiến thể trả về thời gian quy định vào ngày hiện tại , theo báo cáo của các nội dụng chỉ {fn getdateODBC()}:

Kế hoạch thực hiện


1
Vâng, tôi có lẽ sẽ làm CONVERT(DATE, '20141201')nếu nhu cầu của bạn rõ ràng vượt qua tất cả những thứ khác. Sau đó, một lần nữa, nếu cột bên dưới là loại ngày / giờ, điều này thực sự không cần thiết. Bạn có nói WHERE Active = CONVERT(BIT, 1)để tránh WHERE Active = 1bị hiểu là một INT?
Aaron Bertrand

3
@AaronBertrand Trên thực tế, tôi đã được biết là làm chính xác điều đó :) Và đây là một ví dụ về lý do tại sao .
Paul White 9

-1

đoạn mã dưới đây, nhận phiên hiện tại dateformat, gặp lỗi khi chuyển đổi sang datetime, sau đó đặt định dạng ngày thành ymd và cuối cùng nhưng không kém phần kiểm tra chuyển đổi (truyền) một lần nữa và nó hoạt động

-- 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 tháng 10 năm 2016 18:17:30 Bản quyền (c) Microsoft Corporation Enterprise Edition: Cấp phép dựa trên lõi (64-bit) trên Windows Server 2012 R2 Datacenter 6.3 (Bản dựng 9600 :) (Hypervisor)


quan tâm giải thích bỏ phiếu xuống?, mã đang hoạt động tốt ở đây!
Marcello Miorelli
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.