Programing

SQL에서 "0으로 나누기"오류를 피하는 방법은 무엇입니까?

lottogame 2020. 3. 5. 22:46
반응형

SQL에서 "0으로 나누기"오류를 피하는 방법은 무엇입니까?


이 오류 메시지가 있습니다 :

메시지 8134, 수준 16, 상태 1, 줄 1 오류 0 발생으로 나눕니다.

이 오류 메시지가 다시 표시되지 않도록 SQL 코드를 작성하는 가장 좋은 방법은 무엇입니까?

다음 중 하나를 수행 할 수 있습니다.

  • 제수가 제로가 아닌 where 절을 추가하십시오.

또는

  • case statement를 추가하여 0에 대한 특별한 처리가 가능합니다.

NULLIF을 사용하는 가장 좋은 방법 입니까?

더 좋은 방법이 있습니까, 아니면 어떻게 시행 할 수 있습니까?


"0으로 나누기"오류를 피하기 위해 다음과 같이 프로그래밍했습니다.

Select Case when divisor=0 then null
Else dividend / divisor
End ,,,

그러나 여기에 훨씬 좋은 방법이 있습니다.

Select dividend / NULLIF(divisor, 0) ...

이제 유일한 문제는 "/"키를 사용하는 경우 NullIf 비트를 기억하는 것입니다.


0을 반환하려는 경우 0 수정이 발생할 경우 다음을 사용할 수 있습니다.

SELECT COALESCE(dividend / NULLIF(divisor,0), 0) FROM sometable

제수가 0 인 모든 제수에 대해 결과 집합에서 0을 얻습니다.


이것은 데이터에서 발생하는 0으로 나누기를 해결하려고 할 때 내 상황에 가장 적합한 것으로 보였습니다.

다양한 학교 클럽의 남녀 비율을 계산하려고하는데 다음 쿼리가 실패하고 반지가없는 클럽의 비율을 계산하려고 할 때 0으로 나누기 오류가 발생한다고 가정합니다. :

SELECT club_id, males, females, males/females AS ratio
  FROM school_clubs;

이 기능 NULLIF사용하면 0으로 나누지 않아도됩니다. NULLIF두 표현식을 비교하고 같으면 null을 반환하고 그렇지 않으면 첫 표현식을 반환합니다.

다음과 같이 쿼리를 다시 작성하십시오.

SELECT club_id, males, females, males/NULLIF(females, 0) AS ratio
  FROM school_clubs;

주어진 수로 나눈 값 NULLNULL오류가 발생하지 않습니다.


쿼리 시작시이 작업을 수행 할 수도 있습니다.

SET ARITHABORT OFF 
SET ANSI_WARNINGS OFF

따라서 이와 비슷한 100/0것이 있으면 NULL을 반환합니다. 간단한 쿼리에 대해서만이 작업을 수행 했으므로 더 길고 복잡한 쿼리에 어떤 영향을 줄지 모르겠습니다.


오류로 인해 쿼리가 중단되는 것을 최소한 중지하고 NULL0으로 나눈 값을 반환 할 수 있습니다.

SELECT a / NULLIF(b, 0) FROM t 

그러나 많은 찬사를 얻은 다른 답변에 표시된 것처럼 절대로 이것을 0으로 변환하지 않습니다 coalesce. 이것은 수학적 의미에서 완전히 잘못되었으며 응용 프로그램에서 잘못되고 잘못된 결과를 반환 할 수 있으므로 위험합니다.


편집 : 최근에 많은 downvotes가 생겼습니다 ... 그래서이 답변이 가장 최근의 편집을 받기 전에 작성되었다는 메모를 추가한다고 생각했습니다 .null을 반환하는 것이 옵션으로 강조 표시되었습니다. 매우 수용 가능한 것 같습니다. 내 대답 중 일부는 Edwardo와 같은 문제에 대한 답변에서 0을 반환하는 것을 옹호하는 것처럼 보였습니다. 이것은 내가 반대하는 경우입니다.

답변 : 여기에 근본적인 문제가 있다고 생각합니다 .0으로 나누는 것은 합법적이지 않습니다. 뭔가 근본적으로 잘못되었음을 나타냅니다. 0으로 나누면 수학적으로 이해가되지 않는 것을 시도하고 있으므로 얻을 수있는 숫자 대답이 유효하지 않습니다. (이 경우 널을 사용하면 나중에 수학적 계산에 사용될 값이 아니므로 합리적입니다).

따라서 Edwardo는 "사용자가 0을 넣는 경우 어떻게됩니까?"라는 주석을 묻고 0을 돌려주는 것이 좋다고 주장합니다. 사용자가 금액을 0으로 설정하고 0을 반환하려는 경우 비즈니스 규칙 수준에서 코드를 입력하여 해당 값을 포착하고 0을 반환해야합니다. 0.

그것은 미묘한 차이이지만, 다음에 누군가가 함수를 호출하고 올바른 일을 할 것으로 기대하기 때문에 수학적으로 정확하지 않은 펑키 한 일을하지만, 특정 엣지 케이스를 처리하기 때문에 중요합니다. 나중에 누군가를 물릴 수있는 좋은 기회입니다. 당신은 실제로 0으로 나누지 않습니다 ... 당신은 단지 나쁜 질문에 대한 잘못된 대답을 반환합니다.

내가 무언가를 코딩하고 있다고 상상해보십시오. 나는 방사선 측정 스케일링 값으로 읽어야하지만 이상한 가장자리 케이스에서는 예상하지 못했지만 0을 읽습니다. 그런 다음 내 값을 함수에 버립니다 ... 당신은 나에게 0을 반환합니다! 만세, 방사선 없음! 그것이 실제로 있다는 것을 제외하고는 내가 나쁜 가치를 전달하고 있다는 것입니다 ...하지만 나는 모른다. 뭔가 잘못되었다는 표시이기 때문에 나누기가 오류를 던지기를 원합니다.


SELECT Dividend / ISNULL(NULLIF(Divisor,0), 1) AS Result from table

nullif ()를 사용하여 0을 잡고, 결과로 null을 isull ()을 사용하면 0으로 나누기 오류를 피할 수 있습니다.


"0으로 나누기"를 0으로 바꾸는 것은 논란의 여지가 있지만 유일한 옵션은 아닙니다. 어떤 경우에는 1로 대체하는 것이 (합리적으로) 적절합니다. 나는 종종 내 자신을 발견

 ISNULL(Numerator/NULLIF(Divisor,0),1)

점수 / 횟수의 변화를보고 데이터가없는 경우 기본값을 1로 설정하려고합니다. 예를 들어

NewScore = OldScore *  ISNULL(NewSampleScore/NULLIF(OldSampleScore,0),1) 

종종이 비율을 다른 곳에서 계산했습니다. (낮은 분모에 대해 매우 큰 조정 요소를 던질 수 있기 때문입니다.이 경우 일반적으로 OldSampleScore를 제어하는 ​​것이 임계 값보다 크므로 0을 배제합니다. 그러나 때때로 '핵'이 적합합니다.


저장 프로 시저에 대해 처리하기 위해 함수를 잠시 작성했습니다 .

print 'Creating safeDivide Stored Proc ...'
go

if exists (select * from dbo.sysobjects where  name = 'safeDivide') drop function safeDivide;
go

create function dbo.safeDivide( @Numerator decimal(38,19), @divisor decimal(39,19))
   returns decimal(38,19)
begin
 -- **************************************************************************
 --  Procedure: safeDivide()
 --     Author: Ron Savage, Central, ex: 1282
 --       Date: 06/22/2004
 --
 --  Description:
 --  This function divides the first argument by the second argument after
 --  checking for NULL or 0 divisors to avoid "divide by zero" errors.
 -- Change History:
 --
 -- Date        Init. Description
 -- 05/14/2009  RS    Updated to handle really freaking big numbers, just in
 --                   case. :-)
 -- 05/14/2009  RS    Updated to handle negative divisors.
 -- **************************************************************************
   declare @p_product    decimal(38,19);

   select @p_product = null;

   if ( @divisor is not null and @divisor <> 0 and @Numerator is not null )
      select @p_product = @Numerator / @divisor;

   return(@p_product)
end
go

  1. Divisor0이 아닌 강제 CHECK 제약 조건 추가
  2. 사용자가이 필드에 0 값을 입력 할 수 없도록 양식에 유효성 검증기를 추가하십시오.

업데이트 SQL의 경우 :

update Table1 set Col1 = Col2 / ISNULL(NULLIF(Col3,0),1)

매직 전역 설정 '0 예외로 나누기 해제'는 없습니다. x / 0의 수학적 의미가 NULL 의미와 다르기 때문에 연산을 처리해야하므로 NULL을 리턴 할 수 없습니다. 나는 당신이 명백한 것을 돌보고 있다고 가정하고 쿼리에는 0 제수가있는 레코드를 제거하고 나누기를 평가하지 않아야하는 조건이 있다고 가정합니다. 대부분의 개발자들이 SQL은 절차 적 언어처럼 행동 할 것으로 예상하고 논리 연산자 단락을 제공하는보다 일반적인 '잡았다'이지만 않습니다 NOT . 이 기사를 읽는 것이 좋습니다. http://www.sqlmag.com/Articles/ArticleID/9148/pg/2/2.html


다음은 0으로 나눌 수있는 상황입니다. 비즈니스 규칙은 재고 회전을 계산하기 위해 일정 기간 동안 판매 된 상품 비용을 가져 와서 연간 화하는 것입니다. 연간 번호를 얻은 후에는 해당 기간의 평균 재고로 나눕니다.

3 개월 동안 발생하는 재고 회전 수를 계산하려고합니다. 본인은 3 개월 동안 1,000 달러의 재화가 판매 된 것으로 계산했습니다. 연간 판매율은 $ 4,000 ($ 1,000 / 3) * 12입니다. 초기 재고는 0입니다. 최종 재고는 0입니다. 내 평균 재고는 이제 0입니다. 매년 $ 4000의 매출이 있으며 재고는 없습니다. 이것은 무한한 회전 수를 산출합니다. 이는 고객이 내 모든 재고를 변환하고 구매 함을 의미합니다.

이것은 재고 회전을 계산하는 방법에 대한 비즈니스 규칙입니다.


where 절을 사용하여 데이터를 필터링하여 0 값을 얻지 않도록합니다.


CREATE FUNCTION dbo.Divide(@Numerator Real, @Denominator Real)
RETURNS Real AS
/*
Purpose:      Handle Division by Zero errors
Description:  User Defined Scalar Function
Parameter(s): @Numerator and @Denominator

Test it:

SELECT 'Numerator = 0' Division, dbo.fn_CORP_Divide(0,16) Results
UNION ALL
SELECT 'Denominator = 0', dbo.fn_CORP_Divide(16,0)
UNION ALL
SELECT 'Numerator is NULL', dbo.fn_CORP_Divide(NULL,16)
UNION ALL
SELECT 'Denominator is NULL', dbo.fn_CORP_Divide(16,NULL)
UNION ALL
SELECT 'Numerator & Denominator is NULL', dbo.fn_CORP_Divide(NULL,NULL)
UNION ALL
SELECT 'Numerator & Denominator = 0', dbo.fn_CORP_Divide(0,0)
UNION ALL
SELECT '16 / 4', dbo.fn_CORP_Divide(16,4)
UNION ALL
SELECT '16 / 3', dbo.fn_CORP_Divide(16,3)

*/
BEGIN
    RETURN
        CASE WHEN @Denominator = 0 THEN
            NULL
        ELSE
            @Numerator / @Denominator
        END
END
GO

때로는 0이 적절하지 않을 수도 있지만 때로는 1도 적절하지 않을 수도 있습니다. 때로는 1 또는 100 %의 변화로 설명되는 0에서 100,000,000으로의 점프도 오해의 소지가 있습니다. 이 시나리오에서는 100,000,000 %가 적합 할 수 있습니다. 백분율 또는 비율을 기반으로 어떤 결론을 내릴 것인지에 따라 다릅니다.

예를 들어, 판매량이 2-4 개에서 매우 작은 판매 품목과 1,000,000에서 2,000,000 개로 변경된 매우 큰 판매 품목은 분석 가나 경영진에게 매우 다른 의미를 지닐 수 있지만 둘 다 100 % 또는 1 개로 나옵니다. 변화.

합법적 인 데이터와 혼합 된 0 % 또는 100 % 행을 검색하는 것보다 NULL 값을 분리하는 것이 더 쉬울 수 있습니다. 분모의 0은 종종 오류 또는 결 측값을 나타낼 수 있으며, 데이터 세트를 깔끔하게 보이도록 임의의 값을 채우고 싶지 않을 수도 있습니다.

CASE
     WHEN [Denominator] = 0
     THEN NULL --or any value or sub case
     ELSE [Numerator]/[Denominator]
END as DivisionProblem

호출 프로그램으로 다시 전파 될 때 오류를 적절하게 처리 할 수 ​​있습니다 (또는 원하는 경우 무시하십시오). C #에서 SQL에서 발생하는 모든 오류는 다른 오류와 마찬가지로 코드를 잡고 처리 할 수있는 예외를 throw합니다.

나는 당신이 오류를 숨기고 싶지 않다는 점에서 Beska에 동의합니다. 원자로를 다루지 않을 수도 있지만 일반적으로 오류를 숨기는 것은 나쁜 프로그래밍 관행입니다. 이것이 대부분의 최신 프로그래밍 언어가 실제 반환 값을 오류 / 상태 코드와 분리하기 위해 구조적 예외 처리를 구현하는 이유 중 하나입니다. 수학을 할 때 특히 그렇습니다. 가장 큰 문제는 올바르게 계산 된 0이 반환되거나 오류의 결과로 0을 구분할 수 없다는 것입니다. 대신 반환 된 값은 계산 된 값이며, 문제가 발생하면 예외가 발생합니다. 물론 데이터베이스에 액세스하는 방법과 사용중인 언어에 따라 달라 지지만 항상 처리 할 수있는 오류 메시지를 얻을 수 있어야합니다.

try
{
    Database.ComputePercentage();
}
catch (SqlException e)
{
    // now you can handle the exception or at least log that the exception was thrown if you choose not to handle it
    // Exception Details: System.Data.SqlClient.SqlException: Divide by zero error encountered.
}

사용 NULLIF(exp,0)하지만 이런 식으로-NULLIF(ISNULL(exp,0),0)

NULLIF(exp,0)exp가 null있지만 NULLIF(ISNULL(exp,0),0)중단되지 않으면 중단


이것이 내가 고친 방법입니다.

IIF (ValueA! = 0, 총계 / ValueA, 0)

업데이트로 감쌀 수 있습니다.

SET Pct = IIF (ValueA! = 0, 총계 / ValueA, 0)

또는 선택 :

SELECT IIF (ValueA! = 0, Total / ValueA, 0) AS Pct FROM Tablename;

생각?

참고 URL : https://stackoverflow.com/questions/861778/how-to-avoid-the-divide-by-zero-error-in-sql



반응형