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 2023년 1월 - SQLER의 업데이트 강좌 리스트 코난(김대우) 2023.01.02 527
» NULL 데이터가 포함된 데이터 사용 시 주의점 jevida(강성욱) 2017.01.11 2874
2045 통계정보와 실제 데이터 분포 확인하기 jevida(강성욱) 2017.01.11 1704
2044 SQL Server Failover Cluster 설치시 네트워크 이름으로 인한 설치 오류 jevida(강성욱) 2017.01.11 1493
2043 SSIS 실행 로그 남기기 jevida(강성욱) 2017.01.11 2734
2042 확장이벤트를 사용한 실행 계획 캡처 jevida(강성욱) 2017.01.11 1243
2041 테이블 외래키 트리 확인 및 데이터 삭제하기 jevida(강성욱) 2017.01.11 1946
2040 기본 추적을 사용한 SQL Server 스키마 변경사항 캡처 jevida(강성욱) 2017.01.11 1619
2039 SQL Server 특정 테이블의 모든 컬럼에서 문자열 찾기 jevida(강성욱) 2017.01.11 1731
2038 SQL Server 임시 테이블 특성 jevida(강성욱) 2017.01.11 7239
2037 트리거를 사용하여 특정 컬럼 업데이트 하기 (After 트리거) jevida(강성욱) 2017.01.11 2217
2036 컬럼스토어 인덱스 성능 (Columnsotre Index Performance) jevida(강성욱) 2017.01.11 3227
2035 In-Memory 최적화 파일 경로 변경 jevida(강성욱) 2017.01.11 1524
2034 SA 계정 이름 변경 및 비활성화 jevida(강성욱) 2017.01.11 2364
2033 SQL Server 데이터베이스 속성을 확인할 때 사용되는 master.dbo.spt_values 복구 jevida(강성욱) 2017.01.11 1632
2032 XML로 생성된 정보를 테이블로 저장하기(sp_xml_preparedocument) jevida(강성욱) 2017.01.11 2416
2031 SQL Server 2016 Temporal Table – 데이터 변경 내용 추적 jevida(강성욱) 2017.01.11 1948
2030 Error 17053 타사 네트워크 장치의 SMB 파일 공유 오류 jevida(강성욱) 2017.01.11 1189
2029 최신 SQL Server Native Client 설치하기 jevida(강성욱) 2017.01.11 1861
2028 Sys.dm_os_waiting_tasks를 활용한 실행중인 병렬쿼리 확인 jevida(강성욱) 2017.01.11 1589
2027 DBCC DROPCLEANBUFFERS가 작동하지 않을 때 jevida(강성욱) 2017.01.11 1712





XE Login