NULL 데이터가 포함된 데이터 사용 시 주의점

 

  • Version : SQL Server 2005, 2008, 2008R2, 2012, 2014, 2016

 

SQL Server에서 NULL은 0 값도 아니며 공백도 아닌 그냥 빈 값이다. 데이터를 조회할 때 NULL 값이 포함된 경우 사용자가 원하는 값이 조회되지 않을 수 있다. NULL이 포함된 데이터 조회시 어떻게 값이 다르게 표현되는지 알아본다.

 

실습에 사용된 옵션은 시스템 기본값인 SET ANSI_NULLS ON 상태이다.

 

아래 스크립트는 두 개의 테이블을 생성하여 #TestTableB 테이블에만 NULL값이 포함된 데이터를 입력 한다.

--Creating the first temporary table for testing

IF(OBJECT_ID('tempdb..#TestTableA') IS NOT NULL)

    DROP TABLE #TestTableA

 

CREATE TABLE #TestTableA (ID INT)

 

--Creating the second temporary table for testing

IF(OBJECT_ID('tempdb..#TestTableB') IS NOT NULL)

    DROP TABLE #TestTableB

 

CREATE TABLE #TestTableB (ID INT)

 

--Inserting data

INSERT INTO #TestTableA(ID) VALUES (1),(2),(5),(9),(3)

INSERT INTO #TestTableB(ID) VALUES (2),(7),(NULL),(9)

 

[NOT IN 주의]

쿼리는 서브쿼리를 사용하여 #TestTableB 테이블에 포함되어 있지 않는 데이터를 #TestTableA 에서 조회한다. 조회된 결과가 예상했던 결과인가? #TestTableB 테이블의 NULL 값으로 인해 빈 결과 집합을 생성한다.

--Checking the result of the query

SELECT ID FROM #TestTableA WHERE ID NOT IN (SELECT ID FROM #TestTableB)    

 

 

 

[집계 함수 사용시 주의]

NULL 값이 포함된 경우 집계 함수에서 결과가 다르게 표시될 수 있다. Count(*) 과 Count(Column) 의 조회 결과가 어떻게 다른지 알아본다. 테스트 테이블을 생성한다.

--Creating temporary table for testing

IF(OBJECT_ID('tempdb..#TestTable') IS NOT NULL)

    DROP TABLE #TestTable

GO

 

CREATE TABLE #TestTable ( ID INT )

 

Count(id), count(*)의 집계 결과를 조회한다. 결과가 다른 것을 확인할 수 있다. 집계 함수에 컬럼을 사용할 경우 NULL 값은 무시되는 것을 확인할 수 있다.

--Inserting data for testing

INSERT INTO #TestTable(ID) VALUES (1),(2),(NULL),(4)

 

SELECT COUNT(ID) AS 'The result of Count(ID)' FROM #TestTable

 

SELECT COUNT(*) AS 'The result of Count(*)' FROM #TestTable

 

 

 

--Inserting only NULLs

TRUNCATE TABLE #TestTable

 

INSERT INTO #TestTable(ID) VALUES (NULL),(NULL),(NULL),(NULL)

 

SELECT COUNT(ID) AS 'The result of Count(ID) when ID column contains only NULL values' FROM #TestTable

 

SELECT COUNT(*) AS 'The result of Count(*) when ID column contains only NULL values' FROM #TestTable

 

 

 

--Min , Max, AVG

TRUNCATE TABLE #TestTable

 

INSERT INTO #TestTable(ID) VALUES (1),(2),(NULL),(4),(5)

 

SELECT AVG(ID) AS Average FROM #TestTable

 

 

 

[변수 사용시 주의]

SET을 사용하여 변수에 값을 할당 할 때 SELECT 방법에 따라 결과 값이 다르게 나타난다.

DECLARE @var INT= 0

 

--Creating temporary table for testing

IF(OBJECT_ID('tempdb..#TestTable') IS NOT NULL)

    DROP TABLE #TestTable

 

CREATE TABLE #TestTable (ID INT)

 

--Inserting data for testing

INSERT INTO #TestTable(ID) VALUES (1),(2),(4)

 

--Using SELECT

SELECT @var=ID FROM #TestTable WHERE ID=3

 

SELECT @var AS 'Variable value after SELECT'

 

--Using SET

SET @var = ( SELECT ID FROM #TestTable WHERE ID=3 )

 

SELECT @var AS 'Variable value after SET'

 

 

 

DECLARE @var1 INT, @var2 INT=7

 

SET @var2 = @var1 + @var2

 

SELECT @var2 As Variable2    

 

 

 

[Group by 주의]

NULL을 포함한 그룹화에 NULL 값은 결과 집합의 하나로 포함된다.

IF(OBJECT_ID('tempdb..#TestTable') IS NOT NULL)

    DROP TABLE #TestTable

GO

 

CREATE TABLE #TestTable ( ID INT, Value INT )

 

INSERT INTO #TestTable(ID, Value) VALUES (1, 10),(2, 20),(NULL, 70),(NULL, 90),(4, 50),(5, 60)

 

SELECT ID, AVG(Value) AS Average, MAX(Value) As Maximum

FROM #TestTable

GROUP BY ID    

 

 

 

[<> (!=) 비교]

비교 연산자의 경우 대부분 NULL 값과 상수 비교 시 동일하지 않기 때문에 아래 결과가 ture 일거라고 생각한다. 하지만 알 수 없는 값과 비교 시 항상 false를 반환한다. (SET ANSI_NULLS OFF 경우 결과는 다르게 나타난다.)

 

DECLARE @a INT=1,

@b INT

 

SELECT @a AS a, @b AS b

 

IF(@a <> @b)

    SELECT 1 AS result

ELSE

    SELECT 0 AS result

 

 

 

NULL 작업을 할 때 일부 혼란스러운 경우가 있으므로 반드시 NULL 데이터 여부를 확인하고 원하는 값을 조회 할 수 있도록 특성을 파악해서 사용하는 것이 중요하다.

 

 

[참고자료]

https://www.mssqltips.com/sqlservertip/4082/some-tricky-situations-when-working-with-sql-server-nulls/

 




강성욱 / jevida@naver.com
Microsoft SQL Server MVP
Blog : http://sqlmvp.kr
Facebook : http://facebook.com/sqlmvp

No. Subject Author Date Views
Notice SQL강좌: 챗GPT와 함께 배우는 SQL Server 무료 강좌 목차와 소개 (2023년 9월 업데이트) 코난(김대우) 2023.08.18 34037
Notice Python 무료 강좌 - 기초, 중급, 머신러닝(2023년 6월 업데이트) 코난(김대우) 2021.01.01 17172
2054 확장이벤트 사용시 주의사항 jevida(강성욱) 2017.01.11 1703
2053 Unix(Linux) timestamp(bigint) 형식을 datetime 으로 변경하기 jevida(강성욱) 2017.01.11 3731
2052 Query Rule Off를 사용한 SQL Server 옵티마이저 비활성화 jevida(강성욱) 2017.01.11 1675
2051 시스템 관리자 권한이 없는 특정 프로그램에서 Trace Flag 사용하기 jevida(강성욱) 2017.01.11 2587
2050 In-Memory OLTP 환경에서 체크포인트 작업과 디스크 부족 경고 jevida(강성욱) 2017.01.11 1335
2049 Spool 연산자와 추적 플래그 8690 jevida(강성욱) 2017.01.11 2020
2048 .NET 4.6.1에 변경된 Multisubnet 기본 수신기 동작 jevida(강성욱) 2017.01.11 1666
2047 함수 통계 정보 확인 (sys.dm_exec_function_stats) jevida(강성욱) 2017.01.11 1635
» NULL 데이터가 포함된 데이터 사용 시 주의점 jevida(강성욱) 2017.01.11 2960
2045 통계정보와 실제 데이터 분포 확인하기 jevida(강성욱) 2017.01.11 1739
2044 SQL Server Failover Cluster 설치시 네트워크 이름으로 인한 설치 오류 jevida(강성욱) 2017.01.11 1564
2043 SSIS 실행 로그 남기기 jevida(강성욱) 2017.01.11 2798
2042 확장이벤트를 사용한 실행 계획 캡처 jevida(강성욱) 2017.01.11 1298
2041 테이블 외래키 트리 확인 및 데이터 삭제하기 jevida(강성욱) 2017.01.11 2060
2040 기본 추적을 사용한 SQL Server 스키마 변경사항 캡처 jevida(강성욱) 2017.01.11 1673
2039 SQL Server 특정 테이블의 모든 컬럼에서 문자열 찾기 jevida(강성욱) 2017.01.11 1779
2038 SQL Server 임시 테이블 특성 jevida(강성욱) 2017.01.11 7286
2037 트리거를 사용하여 특정 컬럼 업데이트 하기 (After 트리거) jevida(강성욱) 2017.01.11 2282
2036 컬럼스토어 인덱스 성능 (Columnsotre Index Performance) jevida(강성욱) 2017.01.11 3276
2035 In-Memory 최적화 파일 경로 변경 jevida(강성욱) 2017.01.11 1559





XE Login