2015-04-29 2 views
1

Был поиск часа и не нашел ответа, который касается того, что происходит с моим кодом. Этот запрос счастливо возвращает данные:Ошибка арифметического переполнения преобразования varchar в тип данных числовой - не удается найти ответ, который соответствует

WITH RESULTS_TEST_COMPONENT_CTE(RequisitionNumber, ResultsName, ResultValue) 
AS 
(
SELECT [RequisitionNumber] 
     ,[ResultsName] 
     ,LTRIM(RTRIM([ResultValue])) 
FROM dbo.RESULTS_TEST_COMPONENT 
--WHERE TestGroupNumber IN ('T4367', 'T8033') 
WHERE SUBSTRING(RequisitionNumber, 2, 4) = '1521' 
AND ResultsName = 'HEMOGLOBIN A1C' 
AND ISNUMERIC(ResultValue) = 1 
) 
, RESULTS_TEST_COMPONENT_CTE2(RequisitionNumber, ResultsName, ResultValue) 
AS 
(
SELECT RequisitionNumber 
     ,ResultsName 
     ,CAST(ResultValue AS decimal(3,1)) AS ResultValue 
FROM RESULTS_TEST_COMPONENT_CTE 
) 
SELECT * FROM RESULTS_TEST_COMPONENT_CTE2 

Вот первые несколько строк данных, так же, как и ожидалось:

RequisitionNumber ResultsName  ResultValue 
C1521020510   HEMOGLOBIN A1C 5.9 
C1521044250   HEMOGLOBIN A1C 5.4 
C1521123010   HEMOGLOBIN A1C 5.6 
C1521121420   HEMOGLOBIN A1C 5.8 
C1521102210   HEMOGLOBIN A1C 13.2 

Однако, когда я изменить последнюю строку на это, он бросает «Арифметическое переполнение ошибка преобразования varchar в тип данных числовой. " Ошибка:

SELECT * FROM RESULTS_TEST_COMPONENT_CTE2 
WHERE CAST(ResultValue AS decimal(3,1)) BETWEEN 7.5 AND 10 

Я также получаю сообщение об ошибке, если я оставить из «CAST» и просто поставить WHERE ResultValue BETWEEN 7.5 AND 10. Я подтвердил, что все данные в столбце ResultValue являются числовыми в формате 00.0. Мне нужно, чтобы предложение WHERE выполняло требование, чтобы значение было> = 7.5 и < = 10. Я ожидал, что он вернет эту последнюю строку, где ResultValue - 13.2, но вместо этого я получаю эту ошибку. Может ли кто-нибудь сказать мне, в чем проблема?

(я получаю сообщение об ошибке, если я использую только первый КТР.)

+0

Что делать, если вы попробуете «МЕЖДУ CAST» (7.5 AS decimal (3,1)) И CAST (10 AS decimal (3,1)) »вместо BETWEEN 7.5 И 10 –

ответ

2

Есть две проблемы здесь, то первое, что только потому, что что-то проходит, как ISNUMERIC не означает, что вы можете преобразовать его в десятичное. Ниже будет возвращать 1:

SELECT ISNUMERIC('1,0'); 

Но это возвращает ошибку:

SELECT CAST('1,0' AS DECIMAL(3, 1)) 

Другой, менее очевидная проблема заключается в том, что вы не имеете никакого контроля над тем, когда SQL Server будет выполнять какую операцию, так что даже хотя у вас есть ISNUMERIC(resultValue) = 1 в первом CTE, нет гарантии, что он будет выполнен до WHERE CAST(ResultValue AS decimal(3,1)) BETWEEN 7.5 AND 10. Таким образом, вы все равно можете пытаться (и не выполнять) отличать числовые значения до десятичной. Он работает, когда он находится в SELECT, потому что он оценивается после WHERE.

Существует два способа обойти это, последнее - взломать.Предпочтительный способ вокруг него, чтобы заставить материализации первого запроса, используя временную таблицу:

CREATE TABLE #ResultsComponent 
(
    [RequisitionNumber] VARCHAR(50), 
    [ResultsName] VARCHAR(50), 
    ResultValue DECIMAL(3, 1) 
); 
INSERT #ResultsComponent (RequisitionNumber, ResultsName, ResultValue) 
SELECT [RequisitionNumber] 
     ,[ResultsName] 
     ,LTRIM(RTRIM([ResultValue])) 
FROM dbo.RESULTS_TEST_COMPONENT 
--WHERE TestGroupNumber IN ('T4367', 'T8033') 
WHERE SUBSTRING(RequisitionNumber, 2, 4) = '1521' 
AND ResultsName = 'HEMOGLOBIN A1C' 
AND ISNUMERIC(ResultValue) = 1; 

SELECT RequisitionNumber, ResultsName, ResultValue 
FROM #ResultsComponent 
WHERE ResultValue BETWEEN 7.5 AND 10; 

Другой способ, который является хак, а не гарантируется работа заключается в использовании TOP, чтобы заставить порядок выполнение, но убедитесь, что вы выбираете значение достаточно высоко, что это не влияет на результаты:

WITH RESULTS_TEST_COMPONENT_CTE(RequisitionNumber, ResultsName, ResultValue) 
AS 
(
SELECT TOP (100000) 
     [RequisitionNumber] 
     ,[ResultsName] 
     ,LTRIM(RTRIM([ResultValue])) 
FROM dbo.RESULTS_TEST_COMPONENT 
--WHERE TestGroupNumber IN ('T4367', 'T8033') 
WHERE SUBSTRING(RequisitionNumber, 2, 4) = '1521' 
AND ResultsName = 'HEMOGLOBIN A1C' 
AND ISNUMERIC(ResultValue) = 1 
) 
SELECT RequisitionNumber 
     ,ResultsName 
     ,CAST(ResultValue AS decimal(3,1)) AS ResultValue 
FROM RESULTS_TEST_COMPONENT_CTE 
WHERE CAST(ResultValue AS decimal(3,1)) BETWEEN 7.5 AND 10; 

Мы можем воссоздать эту проблему довольно просто:

CREATE TABLE #T (ResultValue VARCHAR(100)) 
INSERT #T (ResultValue) 
SELECT t.ResultValue 
FROM sys.all_objects o 
     CROSS APPLY (VALUES (CAST(Name AS VARCHAR(100))), (CAST(object_id AS VARCHAR(100)))) t (ResultValue); 

WITH CTE AS 
( SELECT ResultValue = CAST(ResultValue AS INT) 
    FROM #T 
    WHERE ISNUMERIC(ResultValue) = 1 
) 
SELECT * 
FROM CTE 
WHERE ResultValue BETWEEN 1 AND 10; 

А потом посмотреть предполагаемый план (не может получить реальный план из-за ошибки) и видим, что оба предиката (ISNUMERIC и BETWEEN 1 AND 10) оцениваются в то же время:

enter image description here

Добавляя вершины, мы можем изменить план, таким образом, что начальные фильтры таблицы сканирования для ISNUMERIC, а затем использует дополнительный шаг для обеспечения BETWEEN 1 AND 10 применяются только к числовым данным:

WITH CTE AS 
( SELECT TOP (1000000) ResultValue = CAST(ResultValue AS INT) 
    FROM #T 
    WHERE ISNUMERIC(ResultValue) = 1 
) 
SELECT * 
FROM CTE 
WHERE ResultValue BETWEEN 1 AND 10; 

enter image description here

+0

Спасибо Gareth, мне было интересно, если использовать временную таблицу чем CTE будет работать и собирался попробовать. Я обнаружил, что с помощью «ResultValue> = 7.5 И ResultValue <= 10» вместо выполняемой функции BETWEEN. Не знаю, почему. Я могу поменять его на временную таблицу, чтобы быть в безопасности. – JeffK627

Смежные вопросы

 Смежные вопросы