2013-01-04 38 views
16

Tôi có một bảng:SQL Server: lỗi khi chuyển đổi kiểu dữ liệu varchar để số

Account_Code | Desc 
503100  | account xxx 
503103  | account xxx 
503104  | account xxx 
503102A  | account xxx 
503110B  | account xxx 

đâu Account_Code là một varchar.

Khi tôi tạo ra một truy vấn dưới đây:

Select 
    cast(account_code as numeric(20,0)) as account_code, 
    descr 
from account 
where isnumeric(account_code) = 1 

Nó chạy tốt bằng cách trả lại tất cả các kỷ lục mà có một giá trị số hợp lệ trong account_code cột.

Nhưng khi tôi cố gắng thêm một lựa chọn, lồng to SQL trước:

select account_code,descr 
from 
(
    Select cast(account_code as numeric(20, 0)) as account_code,descr 
    from account 
    where isnumeric(account_code) = 1 
) a 
WHERE account_code between 503100 and 503105 

truy vấn sẽ trả về một lỗi

Lỗi chuyển đổi kiểu dữ liệu varchar để số.

Điều gì đang xảy ra ở đó?

Tôi đã chuyển đổi thành số nếu account_code hợp lệ, nhưng có vẻ như truy vấn vẫn đang cố xử lý bản ghi không hợp lệ.

Tôi cần sử dụng mệnh đề BETWEEN trong truy vấn của mình.

+2

gì phiên bản của SQL Server? – ErikE

Trả lời

8

Không đảm bảo rằng SQL Server sẽ không cố gắng thực hiện CONVERT đến numeric(20,0)trước nó chạy bộ lọc trong mệnh đề WHERE.

Và, ngay cả khi nó đã làm, ISNUMERIC là không đủ, vì nó thừa nhận £1d4 như là số, không ai trong số đó có thể được chuyển đổi sang numeric(20,0). (*)

chia nó thành hai truy vấn riêng biệt, đầu tiên trong số đó lọc kết quả và đặt chúng trong bảng tạm thời hoặc biến bảng, phần thứ hai trong số đó thực hiện chuyển đổi. (Truy vấn con và CTE không đủ để ngăn trình tối ưu hóa thử chuyển đổi trước bộ lọc)

Đối với bộ lọc, có thể sử dụng account_code not like '%[^0-9]%' thay vì ISNUMERIC.


(*) ISNUMERIC trả lời cho câu hỏi đó không ai (cho đến nay như tôi biết) đã từng muốn hỏi - "chuỗi này có thể được chuyển đổi sang bất kỳ của kiểu dữ liệu số - Tôi don 't quan tâm nào? " - khi rõ ràng, điều mà hầu hết mọi người muốn hỏi là "chuỗi này có thể được chuyển thành x không?" trong đó xcụ thể kiểu dữ liệu mục tiêu.

25

SQL Server 2012 và sau

Chỉ cần sử dụng Try_Convert thay vì:

TRY_CONVERT mất giá trị truyền cho nó và cố gắng chuyển đổi nó sang data_type quy định.Nếu dàn diễn viên thành công, TRY_CONVERT trả về giá trị như data_type được chỉ định; nếu một lỗi xảy ra, null được trả về. Tuy nhiên, nếu bạn yêu cầu một chuyển đổi rõ ràng là không được phép, thì TRY_CONVERT không thành công với lỗi.

Read more about Try_Convert.

SQL Server 2008 và Đầu

Cách truyền thống xử lý này là do bảo vệ tất cả các biểu hiện với một tuyên bố trường hợp để không có vấn đề khi nó được đánh giá, nó sẽ không tạo ra một lỗi, thậm chí nếu nó hợp lý dường như câu lệnh CASE không cần thiết. Một cái gì đó như thế này:

SELECT 
    Account_Code = 
     Convert(
     bigint, -- only gives up to 18 digits, so use decimal(20, 0) if you must 
     CASE 
     WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL 
     ELSE X.Account_Code 
     END 
    ), 
    A.Descr 
FROM dbo.Account A 
WHERE 
    Convert(
     bigint, 
     CASE 
     WHEN X.Account_Code LIKE '%[^0-9]%' THEN NULL 
     ELSE X.Account_Code 
     END 
    ) BETWEEN 503100 AND 503205 

Tuy nhiên, tôi thích sử dụng chiến lược như thế này với SQL Server 2005 trở lên:

SELECT 
    Account_Code = Convert(bigint, X.Account_Code), 
    A.Descr 
FROM 
    dbo.Account A 
    OUTER APPLY (
     SELECT A.Account_Code WHERE A.Account_Code NOT LIKE '%[^0-9]%' 
    ) X 
WHERE 
    Convert(bigint, X.Account_Code) BETWEEN 503100 AND 503205 

Điều này không được chiến lược chuyển đổi các Account_Code giá trị cho NULL bên trong của bảng X khi chúng không phải là số. Ban đầu, tôi đã sử dụng CROSS APPLY nhưng vì đã chỉ ra một lỗi như vậy vì điều này dẫn đến cùng một lỗi vì trình phân tích cú pháp truy vấn chạy vào cùng một vấn đề chính xác về việc tối ưu hóa nỗ lực của tôi để buộc thứ tự biểu thức (vị từ đẩy xuống đánh bại nó). Bằng cách chuyển sang OUTER APPLY, nó đã thay đổi ý nghĩa thực tế của hoạt động sao cho X.Account_Codecó thể chứa các giá trị NULL trong truy vấn bên ngoài, do đó yêu cầu thứ tự đánh giá thích hợp.

Bạn có thể quan tâm để đọc Erland Sommarskog's Microsoft Connect request về vấn đề đặt hàng đánh giá này. Ông thực tế gọi đó là một lỗi.

Có vấn đề khác ở đây nhưng tôi không thể giải quyết chúng ngay bây giờ.

P.S. Tôi đã có một động não ngày hôm nay. Một thay thế cho "cách truyền thống" mà tôi đề xuất là biểu thức SELECT với tham chiếu bên ngoài, cũng hoạt động trong SQL Server 2000. (Tôi đã nhận thấy rằng kể từ khi học CROSS/OUTER APPLY Tôi đã cải thiện khả năng truy vấn của mình với các phiên bản SQL Server cũ hơn, quá - như tôi nhận được linh hoạt hơn với khả năng "tài liệu tham khảo bên ngoài" của SELECT, ONWHERE khoản)

SELECT 
    Account_Code = 
     Convert(
     bigint, 
     (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%') 
    ), 
    A.Descr 
FROM dbo.Account A 
WHERE 
    Convert(
     bigint, 
     (SELECT A.AccountCode WHERE A.Account_Code NOT LIKE '%[^0-9]%') 
    ) BETWEEN 503100 AND 503205 

Đó là ngắn hơn rất nhiều so với báo cáo kết quả CASE.

+0

Truy vấn có phù hợp với bạn không? Nó không cho tôi trừ khi tôi thay đổi 'cross apply' thành' outer apply'. (SQL Server 2012) –

+0

Tôi có nghĩa là phiên bản hiện tại của bạn không hoạt động. Kế hoạch truy vấn thực hiện quét bảng để kiểm tra mọi hàng. Nếu bạn thay đổi sang bên ngoài, nó sẽ lọc ra các hàng xấu trước khi chuyển đổi. [SQL Fiddle] (http://sqlfiddle.com/#!6/664af/3/0) –

6

Nếu bạn đang chạy SQL Server 2012, bạn cũng có thể sử dụng mới TRY_PARSE() chức năng:

Trả về kết quả của một biểu thức, phiên dịch sang các dữ liệu yêu cầu loại, hoặc null nếu các diễn viên thất bại trong SQL Server 2012. Sử dụng TRY_PARSE chỉ để chuyển đổi từ chuỗi thành ngày/giờ và các loại số.

1

Tôi nghĩ rằng sự cố không nằm trong truy vấn phụ nhưng trong mệnh đề WHERE của truy vấn bên ngoài. Khi bạn sử dụng

WHERE account_code between 503100 and 503105 

SQL server sẽ cố gắng để chuyển đổi tất cả các giá trị trong lĩnh vực ACCOUNT_CODE của bạn để nguyên để kiểm tra nó trong tình trạng cung cấp. Rõ ràng nó sẽ không làm như vậy nếu sẽ có các ký tự không phải số nguyên trong một số hàng.

0

cảm ơn, thử này để thay thế

Select 
    STR(account_code) as account_code_Numeric, 
    descr 
from account 
where STR(account_code) = 1 

Tôi rất sẵn lòng giúp bạn

Các vấn đề liên quan