2014-02-21 3 views
2

Нижеприведенный запрос неожиданно завершается с ошибкой арифметического переполнения.Почему этот запрос SQL не работает

select IsNull(t2.val, 5005) 
from(

SELECT 336.6 as val UNION ALL 
SELECT NULL 

) as t2 

«Арифметическая ошибка переполнения, преобразующая int в тип данных числовой».

Как ни странно, если запрос модифицирован, чтобы удалить NULL и заменить его на ту же величину, как и в нулевой сливаются (5005), она работает без проблем

select IsNull(t2.val, 5005) 
from(

SELECT 336.6 as val UNION ALL 
SELECT 5005 

) as t2 

Кроме того, опуская SELECT, строка NULL, полностью позволяет запрос для запуска без проблем

select IsNull(t2.val, 5005) 
from(

SELECT 336.6 as val 

) as t2 

Если значение сливается в функции IsNull изменяется на целое число, которое достаточно мало, чтобы преобразовать в десятичном в подзапросе без расширения, то запрос выполняется

select IsNull(t2.val, 500) 
from(

SELECT 336.6 as val UNION ALL 
SELECT NULL 

) as t2 

Испытано это как в SQL Server 2005 и SQL Server 2008.

Обычно объединение целых чисел с десятичными знаками бесшовный и SQL Server преобразует как целое число и десятичный в десятичный тип достаточно большой, чтобы вместить обоих. Но по какой-то причине выполняется запрос, в котором кастинг происходит как из UNION, так и из IsNull, приводит к сбою приведения.

Кто-нибудь знает, почему это так?

+3

Ни один из ответов пока не объясняет, почему разница между 'ISNULL' и' COALESCE'. Первая строго возвращает тип данных первого аргумента, второй смотрит на все аргументы. например см. 'DECLARE @X VARCHAR (3) SELECT ISNULL (@ X,« Дольше трех символов »), COALESCE (@ X,« Дольше трех символов »)' –

+2

В вопросе упоминается «COALESCE» кратко, но затем резюме последнего абзаца вопрос только о 'ISNULL'. Но спасибо за хороший комментарий. – Szymon

+0

@ Шимон - Да, ты прав. Должно быть, просто просмотрел, что он видел как «ISNULL», так и «COALESCE», и предположил, что это один из тестовых случаев. –

ответ

6

Попробуйте сделать это

select * into t2 
from(
    SELECT 336.6 as val 
    UNION ALL 
    SELECT NULL 
) as x 

Если вы теперь посмотрите на столбцы, которые Вы видите числовой с цифровой точностью до 4 и масштаба 1

select * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='T2' 

SQL сделал это решение, основанное на самых маленьких числовая точность 336.6. Теперь, когда вы просите его преобразовать NULL в 5005, вы говорите, конвертируете любые значения NULL в число, слишком большое, чтобы вписаться в числовое число с точностью 4 и шкалой 1. Сообщение об ошибке указывает, что 5005 вон ' t fit in Numeric (4,1)

Это будет работать, потому что таблица теперь будет генерировать большее числовое поле, так как SQL необходимо разместить 5005. Создайте таблицу, используя новое содержимое T2 снизу, и тип поля должен перейти к Numeric (5,1), позволяющему 5005 соответствовать.

select IsNull(t2.val, 5005) 
    from(

    SELECT 336.6 as val UNION ALL 
    SELECT 5005 

    ) as t2 

При выполнении оператора без NULL во внутреннем запросе SQL никогда не оценивает 5005, поэтому он не достигает состояния, где он должен поставить 5005 в числовом (4,1) области.

select IsNull(t2.val, 5005) 
from(

SELECT 336.6 as val 

) as t2 
+1

+1 Более приятное объяснение, чем мое. –

+0

Не знаю об этом, но хотел бы, чтобы OP увидела, как выглядит таблица, создаваемая SQL ... – Sparky

+0

Я ожидал, что для UNION будет создано десятичное число 4,1 и IsNull, чтобы расширить выход по требованию. Это не так, но, по-видимому, это происходит! Не знал об этой разнице – Trent

5

Я думаю, что проблема в том, что, когда SQL Server разрешает соединение, он принимает решение о десятичном типе, который только достаточно большой, чтобы соответствовать 333,6 (который decimal(4,1)). Попытка положить 5005 в результате приводит к переполнению.

Вы можете обойти, что определение точности десятичные себя:

select IsNull(t2.val, 5005) 
from(

SELECT CONVERT(DECIMAL(5,1), 336.6) as val UNION ALL 
SELECT NULL 

) as t2 
+0

+1 Более приятное объяснение, чем мое. –

0

Я считаю его потому, что ваша ценность 336,6 в настоящее время выводятся как тип данных NUMERIC.Если вы хотите быть более конкретным, то экспликация превратила его в DECIMAL;

SELECT IsNull(t2.val, CAST(5005 AS DECIMAL)) 
    FROM (
    SELECT CAST(336.6 AS DECIMAL) AS val 

    UNION ALL 

    SELECT NULL 
    ) AS t2 
+3

'NUMERIC' и' DECIMAL' являются синонимами. Проблема в том, что '5005' не вписывается в' NUMERIC (4,1) '/' DECIMAL (4,1) ' –

+0

@Martin - Спасибо за разъяснение. Поэтому, когда вы используете производные таблицы/CTE таким образом с определенными значениями, лучше всего предоставить явное приведение, чтобы ваш столбец был работоспособным/достаточным (так же, как и при определении типов данных столбцов в таблице temp, table переменная или нормальная таблица?) –

+0

Это, вероятно, более эффективная практика, но я, как правило, не исключаю, когда мне определенно нужно что-то отличное от значения по умолчанию. Весьма беспорядочно читать, если у всего есть 'CAST' вокруг него. –

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