SQL Server: как обрабатывать деление на 0 случаев в запросе - PullRequest
0 голосов
/ 18 августа 2010

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

SELECT SUM(convert(decimal(3,2),
          CASE WHEN Status_ID = 1 AND State_ID = 14 THEN 1 ELSE 0 END))/
          SUM(convert(decimal(3,2),CASE WHEN State_ID = 14 THEN 1 ELSE 0 END)) * 100
FROM SomeTable

В нормальном случае это может вернуть что-то вроде 59.77803.Но получается, что есть (справедливо) случаи, когда второй СУММ - знаменатель - может быть 0. Кто-нибудь знает способ учета этого случая и избежать исключения деления на 0?

Iиспользую SQL Server 2005. Спасибо.

Ответы [ 6 ]

2 голосов
/ 19 августа 2010

Я бы, вероятно, использовал NULL для представления не вычисляемого, но это зависит от ваших намерений в проблемной области (код расширен, чтобы показать, что происходит):

SELECT SUM(convert(decimal(3,2),
                   CASE WHEN Status_ID = 1 AND State_ID = 14 THEN 1 ELSE 0 END)
          )
       /
       NULLIF(
           SUM(convert(decimal(3,2),
                       CASE WHEN State_ID = 14 THEN 1 ELSE 0 END)
           )
           , 0
       )
       * 100
FROM SomeTable

Результат, когда в строке нет строк.таблица, в которой знаменатель будет равен нулю, преобразуется в NULL, поэтому все выражение заканчивается на NULL.

Этот код также можно немного упростить (возможно, потому что это уже упрощенная игрушка):

SELECT SUM( convert(decimal(3,2), CASE WHEN Status_ID = 1 THEN 1 ELSE 0 END) )
       /
       NULLIF( convert(decimal(3,2), COUNT(*)), 0 )
       * 100
FROM SomeTable
WHERE State_ID = 14

В качестве примера объединения нескольких разных вещей в один запрос:

SELECT CASE WHEN State_ID = 14 THEN 'State_ID = 14'
            WHEN State_ID IN (1, 2, 3) THEN 'State_ID IN (1, 2, 3)'
            ELSE 'DEFAULT'
       END AS Category
       ,SUM(convert(decimal(3,2),
                   CASE WHEN Status_ID = 1 THEN 1 ELSE 0 END)
          )
       /
       convert(decimal(3,2), COUNT(*))
       * 100
FROM SomeTable
GROUP BY CASE WHEN State_ID = 14 THEN 'State_ID = 14'
            WHEN State_ID IN (1, 2, 3) THEN 'State_ID IN (1, 2, 3)'
            ELSE 'DEFAULT'
       END
1 голос
/ 19 августа 2010

Если я правильно читаю, это должно работать:

SELECT SUM(convert(decimal(3,2), 
          CASE WHEN Status_ID = 1 THEN 1 ELSE 0 END))/ 
          SUM(convert(decimal(3,2),1)) * 100 
FROM SomeTable 
WHERE State_ID = 14 
1 голос
/ 18 августа 2010

спросите себя, какой результат вы хотите вернуть, когда знаменатель равен 0?
, а затем на основании этого исправьте запрос соответствующим образом.

хорошего встроенного способа сделать это не существует.Это бизнес-логика.

0 голосов
/ 19 августа 2010

Реальный вопрос в том, каким должен быть «ответ», когда вы пытаетесь разделить на ноль?Я бы сделал это NULL, поэтому попробуйте что-то вроде этого:

DECLARE @x int

set @x=5
select 1.0/NULLIF(@x,0)

set @x=0
select 1.0/NULLIF(@x,0)

ВЫХОД:

---------------------------------------
0.200000000000

(1 row(s) affected)


---------------------------------------
NULL

(1 row(s) affected)

Ваш код будет:

SELECT SUM(convert(decimal(3,2), CASE
                                     WHEN Status_ID = 1 AND State_ID = 14 THEN 1 
                                     ELSE 0 
                                 END
                  )
          ) 
        /
        SUM(convert(decimal(3,2),CASE
                                     WHEN State_ID = 14 THEN 1 
                                     ELSE NULL   --<<<<<<<<<<<<force null if div by zero
                                 END
                   )
           )
        * 100
FROM SomeTable

Iне совсем понимаю WHEN Status_ID = 1 AND State_ID = 14 THEN 1, но это код ОП.

0 голосов
/ 19 августа 2010

Чтобы избежать исключения деления на 0, вы можете использовать функцию, которая принимает числитель, знаменатель и значение по умолчанию для передачи, когда знаменатель равен 0. И вызывает эту функцию для деления.

CREATE FUNCTION [dbo].[fn_divide] 
(
    @numerator DECIMAL(3,2),@denominator DECIMAL(3,2),@default DECIMAL(3,2)
)  
RETURNS DECIMAL(3,2) 

AS  BEGIN

 DECLARE @result DECIMAL(3,2)

 IF @denominator = 0
 BEGIN
    set @result=@default
 END
ELSE
 BEGIN
    set @result=@numerator/@denominator
 END 

  return @result
END
0 голосов
/ 18 августа 2010
SELECT SUM(convert(decimal(3,2),
          CASE WHEN Status_ID = 1 AND State_ID = 14 THEN 1 ELSE 0 END))/
          SUM(convert(decimal(3,2),CASE WHEN State_ID = 14 THEN 1 ELSE 0 END)) + 0.00000000001 * 100
FROM SomeTable
...