검수요청.png검수요청.png

위키원
이동: 둘러보기, 검색

(NULL)이란 어떠한 값도 가지지 않고 있다는 뜻이다. 널값이라고도 한다. 널은 빈칸과 같은 개념으로서, 이용할 수 없는, 할당되지 않은, 적용 불가능하다는 의미이다. 널값을 포함한 어떠한 산술식의 결과 역시 널(NULL)이 된다.

개요[편집]

널(null, 간단히 NUL)은 사전적인 뜻으로 '값이 없음'을 나타내는 영단어이며 값이 없다는 것은 그 값이 0조차 아니라는 뜻이다. 독일어에선 숫자 0이란 뜻으로 쓰인다. 독일식으로는 눌, 영미식으로는 널이라고 발음한다. 구조적 질의 언어 (SQL)에서는 데이터베이스 내의 데이터 값이 존재하지 않다는 것을 지시하는데 사용되는 특별한 표시어이다. 데이터베이스 관계 모델의 창시자인 E. F. 콧(E. F. Codd)에 만들어진 개념으로, SQL NULL은 모든 true RDBMS가 빠진 정보와 적용할 수 없는 정보를 지원해야 한다는 요구 사항을 충족시키는 역할을 한다. 콧은 또한 데이터베이스 이론에서 널을 표현하기 위해 그리스어의 오메가의 소문자(ω)를 이용할 것을 도입했다. 널은 SQL에서 널 특수 표시를 구분하기 위해 사용되는 유보된 키워드이다.
널은 SQL 조인에서 사용하기 위해 필수적인 3치 자궁로 인해 논란의 중심이 되어 왔다. 컴퓨터 과학자인 론 밴 더 메이든 교수는 그러한 여러 문제를 다음과 같이 요약했다. SQL 표준에서 비일관성은 SQL에서 널의 취급 문제를 직관적인 논리 어휘 탓으로 돌리는 것은 가능하지 않다는 것을 의미하는 것이다. 이 문제를 해결하기 위해 여러 해법들이 제시되었지만, 대안의 복잡성은 폭넓은 이론 확산의 걸림돌이 되었다.
데이터베이스 비전문가들에게, 널이 뜻하는 바가 무엇인지 이해하는 좋은 방법은 정보적 측면을 기억하는 것이다. “값의 부족”은 0값과 동일한 의미가 아니며, 유사하게 ‘정답의 부족’이라는 말도 정답이 없다는 말과 같은 의미가 아니다. 예를 들어, 다음과 같은 질문을 예로 들어보자. “철수가 책을 몇 권 가지고 있지?” 답은 0(하나도 가지고 있지 않다는 것을 안다면) 또는 널(그가 가지고 있는지, 아닌지 또는 얼마나 가지고 있는지 모름)일 것이다. 데이터베이스 테이블에서, 이러한 답을 가진 컬럼은 널 값으로 시작할 것이다. 그리고 철수가 책을 가지고 있다는 것이 확인되기 전까지는 0으로 수정되지 않을 것이다. 유사하게, 질문이 “철수가 차를 가지고 있어?”라는 질문에도 “모르겠다”라는 정답은 “아니(No)”라는 것과 같은 의미가 아니다. 전자는 데이터베이스의 널 목록을 생성하며, 후자는 데이터베이스 엔트리에서 No를 생성한다.
아스키(ASCII)코드에서는 0번(0x00)에 해당하는 제어 문자이다. 화면상에는 표시되지 않고 오직 바이트 상으로만 존재한다. 단, 윈도우 한정으로 일반적인 텍스트 편집기에서는 고정폭 글꼴을 쓰는 경우에 한해서 공백 비슷하게 표시되는 경우가 있다. 텍스트를 입력하는 필드 같은 데서 바이트 수가 정해져 있는 데, 그 바이트 수를 채우지 못하고 끝내버리면 오류가 발생할 수도 있다. 이런 경우에 바이트 수를 임의로 채우기 위해 나머지 자리에 널 문자를 넣는다. 예를 들면 MP3 파일에 사용되는 ID3 태그 같은 것들이 있다. 일부 프로그램은 널 문자가 아닌 공백 문자(0x20)를 대신 입력하는 경우도 있다. FAT 파일 시스템에서도 공백으로 채운다. 예를 들면, ABCDEFG.TXT라는 파일은 ABCDEFG .TXT와 같이 기록된다. 이외에도 ISO/IEC 646 (아스키), C0 제어 코드, 국제 문자 집합 (유니코드의 U+0000), 확장 이진화 십진법 교환 부호(EBCDIC)와 같은 수많은 문자 집합에 존재한다. 주로 쓰이는 거의 모든 프로그래밍 언어에서 사용할 수 있다.[1][2]

역사[편집]

E. F. 콧(E. F. Codd)은 1975년 그의 논문 'FDT Bulletin of ACM-SIGMOD'에서 널을 빠진 데이터를 나타내는 수단으로서 언급했다. SQL에서 채택된 널을 설명하기 위해 가장 보편적으로 인용되는 콧의 논문은 1979년 논문이었던 'ACM Transactions on Database Systems'으로 비록 이후의 논문에서 많은 제안들 대부분이 모호한 채로 남아있지만, 여기에서 그는 또는 관계 모델/태즈매니아를 소개했다. 1979년 논문 2.3장에는 수학적 연산에서의 널 보급의 세부적인 사항과 널 값을 비교할 때, 3가 논리를 이용하는 비교도 소개를 했다. 여기에는 또한 다른 연산자 셋과 Null을 취급하는 것도 소개를 했으며, 후자의 이슈는 오늘날에도 여전히 논란이 많은 문제이다. 데이터베이 이론계에서는, 1975년과 1979년 제안한 콧의 원래 이론들이 지금은 ‘콧의 테이블’로 언급되고 있다. 콧은 이후에 모든 관계형 데이터베이스 관리 시스템이 빠진 데이터를 지시하기 위해 널에 지원해야할 그의 요구사항을 1985년 '컴퓨터월드 매거진'에 기고한 2개의 글을 통해 보강했다. 1986년 SQL 표준은 근본적으로 IBM 시스템 R에서 수행 프로토타입 이후 콧의 제안을 채택했다. 비록 돈 챔벌린은 널 값을 SQL에서 가장 논쟁적인 특징으로 규정했지만, 그는 빠진 정보를 설명하는 가장 비용이 적게 드는 시스템 지원이라는 실용적인 측면을 들어 SQL에서 널의 설계를 옹호했다.[2]

특징[편집]

중요도[편집]

오라클마이에스큐엘(MySQL)에서는 널을 가장 큰 값으로 간주하고 에스큐엘 서버(SQL SERVER)는 널을 가장 작은 값으로 간주해서 이 기준으로 정렬된다. 근데 이게 맘에 안들어서 널을 가장 마지막에 오게 하도록 하고 싶거나, 내림차순이면 큰수에서 작은 수 이렇게 정렬되어야 하는데 오라클에서 널이 앞에 오는게 싫은 경우 널을 맨 앞이나 뒤로 정렬하는 방법이다.

오라클(ORACLE)

널을 마지막으로 해주는 키워드는 NULLS LAST 이다. 반대 개념으로는 NULLS FIRST를 사용하면 된다.

order by '필드명' desc nulls last
order by '필드명' desc nulls first
마이에스큐엘(MYSQL)

마이에스큐엘에서는 NULLS FIRST, NULLS LAST라는 명령이 없다. 그래서 desc를 두 번 사용해서 구현한다. 널을 먼저 정렬하고, 그 다음 값들을 정렬한다.[3]

1 SELECCT state
2 FROM customers
3 ORDER BY state is null DESC, state desc

데이터베이스[편집]

널은 값이 아니기 때문에 '!=', '^=', '<>', '=' 등의 연산자로는 비교가 불가능하며 컬럼에 데이터가 들어있지 않음을 나타내는 특별한 값이다. 컬럼이 널인 경우만 추출해서 보고 싶은 경우, IS NULL / IS NOT NULL(널이 아닌 경우) 연산자로 널 값인지 알아낼 수 있다. 테이블의 널 허용 필드에 데이터를 넣을 때 빈 문자열(empty string)을 넣은 경우이다.

INSERT INTO tb_test (id, name) VALUES (1, NULL); 
INERRT INTO tb_test (id, name) VALUES (2, "");

오라클에서는 널과 빈 문자열 ""이 모두 널로 처리된다. 아이디(ID)가 1,2인 행의 이름은 모두 널이 들어간다.

SELECT * FROM tb_test WHERE name IS NULL;

위와 같이 쿼리하면 아이디가 1,2인 행이 모두 조회된다. 데이터베이스에서 널은 등호(=)로 비교할 수 없기 때문에 IS NULL / IS NOT NULL 연산자로 판별해야 한다. 마이에스큐엘(MySQL(MariaDB))과 마이크로소프트의 에스큐엘서버(SQLServer)에서는 빈 문자열이 입력된다.

SELECT * FROM tb_test WHERE name = ;

위와 같이 조회하면 아이디가 2인 행이 나온다.

SELECT * FROM tb_test WHERE name IS NULL;

이렇게 조회하면 아이디가 1인 행이 나온다. 각각에서 빈 문자열 필드의 길이를 구해보면 0이 나오게 된다.

-- MySQL 
SELECT LEN(name) FROM tb_test WHERE name = ; 
-- SQLServer
SELECT LENGTH(name) FROM tb_test WHERE name = ;

데이터베이스에서 널을 다룰 때는 항상 주의해야 한다. 계산에 널이 사용되면 0처럼 동작하는 것이 아니라 결과가 널이 된다.[3][4]

연산[편집]

널의 연산 결과를 보여주는 표

널 값을 포함하는 연산의 결괏값 또한 널이다. 모르는 데이터에 숫자를 더하거나 빼도 결과는 마찬가지로 모르는 데이터이기 때문이다. sum(), avg() 등의 함수에 널 값이 있으면 널을 존재하지 않는 행처럼 무시하고 수행한다.[3]

널 보급[편집]

수학적 연산

널은 데이터 값이 아니라, 미지의 값에 대한 표시일 뿐이기 때문에, 널에 수학적 연산을 사용하는 것은 미지의 값으로 나타난다. 아래의 예에서, 10을 널에 곱하면 결과값은 널이 된다.

10 * NULL          -- 결과는 NULL

이것은 예기치 않은 결과를 야기한다. 예를 들어, 널을 0으로 나누려 한다면, 플랫폼은 ‘0으로 나눈’ 예상된 데이터 예외 값을 던지는 대신, 널 값을 반환한다. 비록 이러한 행위가 ISO SQL 표준으로 정의되어 있지는 않지만, 많은 DBMS 벤더들이 이러한 연산을 유사하게 다룬다. 예를 들어, 오라클, PostgreSQL, MySQL 서버, 그리고 마이크로소프트 SQL 서버 플랫폼은 모두 널 값을 아래와 같이 반환한다.

NULL / 0
문자열 연속

SQL에서 흔히 볼 수 있는 문자열 연속 연산 또한 연산자 중 하나가 널 값일 때 널 값으로 나타난다. 아래의 예제는 SQL || 문자열 연속 연산자에 널을 사용함으로써 널 값이 리턴되는 것을 보여준다.

'생선' || NULL || '김치'   -- 결과는 NULL

모든 데이터베이스에서 이러한 것은 아니다. 오라클 RDBMS를 예로 들면, 널 과 빈 문자열은 동일한 것으로 취급되어, '생선' || NULL || '김치'는 ‘생선 김치’로 나타난다.[2]

함수[편집]

NVL(표현식1, 표현식2) (오라클)

ISNULL(표현식1, 표현식2) (에스큐엘 서버)

표현식1 값이 널인 경우 표현식2 값을 반환하며

그렇지 않은 경우 표현식1 값을 반환한다.

표현식1, 표현식2 데이터 타입이 달라도 된다.

자동으로 오라클에서 변환해서 적용해줌

NVL2(표현식1, 표현식2, 표현식3) 표현식1 값이 널이 아닌 경우 표현식2 값을 반환한다.

표현식1이 널인 경우 표현식3을 반환한다.

NULLIF(표현식1, 포현식2) 두 표현식을 비교하여 동일할 경우 널을 반환하고 동일하지 않은 경우 첫번째 식을 반환한다. 수 타입이 아닐 경우 에러남
COALESCE(표현식1, 표현식2, ..., 표현식n) 표현식 목록에서 널이 아닌 첫 번째 표현식을 반환한다. 표현식1이 널이 아니면 표현식1 값을 그렇지 않으면 CHALESCE(표현식2, 표현식3,...)값을 반환한다.
[3]

프로그래밍 언어[편집]

널은 아무것도 없음을 값으로 표현한 것이다. 즉, 빈 값이다. 프로그램이 자료 검생 후 해당 값을 못 찾았을 때, 메모리 할당 실패 등의 경우에, 넘겨주는 반환 값이다. 그러나, 널 문자는 공백 ""(empty) 또는 " "(space)와는 다르다. 단, '=='과 같은 비교 연상자 사용 시에는, 널 공백 ""과 같은 것으로 간주된다. C언어에서는, 널 문자를 문자열의 끝을 나타내는 특수 문자로 쓰인다. 또한 0차원의 난해한 프로그래밍 언어이다. 하나의 양의 정수를 소인수분해하여 명령을 만들어낸다. 아스키코드는 0인데 문자 '0'과는 다르다. 문자 '0'의 아스키코드값은 48 유니코드는 U+0000이 널 문자다. C 프로그래밍에서는 '존재하지 않는 메모리 주소'를 널로 나타낸다. 아래와 같이 특정 포인터 변수를 초기화할 때는 실제로 메모리를 할당하기 전에 널로 초기화해야 한다.

char *ptr = NULL;
ptr = (char*)malloc(...);

포인터 변수와 관련된 연산에서 값이 0인 정수 상수식이 나오면 이를 곧 널로 해석한다. 사실 널은 0, 혹은 (void*)0과 같도록 정의되어야 한다. 따라서 위의 코드는 아래와 같이 써도 된다.

char *ptr = 0;
ptr = (char*)malloc(...);

흔히 아스키나 유니코드 상에서 널의 문자 값이 0이라 메모리 주소로 사용할 때의 널도 0일 거라고 생각하고 코딩을 하는 경우가 있는데 C 표준은 코드상에 직접 나타나는 0에 널이란 의미를 부여하고 있지만, 내부적으로 실제로 널 값이 뭐가 되는지는 규정하지 못하므로 위험하다. 이는 프로그램이 컴파일되는 시스템이 결정하며, 따라서 NULL != 0x0인 경우가 있다. 이런 경우에는 위의 char *ptr = 0;이란 코드가 실제로 ptr에 주솟값 0x0을 대입하지 않는다. 그 코드 자체가 '시스템에 정의된 NULL 값을 대입한다'라는 의미지 0x0을 대입한다는 의미가 아니기 때문이다. 하지만 워낙 많은 시스템에서 널을 0으로 정의하고 있었고, 오래전부터 내려온 코드들에 대해 호환성을 가지기 위해 개발 환경이 맞춰지다 보니 이제는 0이 아닌 환경을 생각도 하기 힘들어졌다. 다만 코드의 구체성을 높이기 위해서 0과 널을 다르게 만드는 프로그래밍 언어도 있다. 자바, 자바스크립트, PHP에서는 널은 널이지 0으로 대체할 수 없다. 단, PHP의 경우 주의할 사항이 있다.

$test = NULL;
print ( $test == 0 ) ? "true" : "false" ; // (1)
print ( $test === 0 ) ? "true" : "false" ; // (2)

위와 같이 비교하면 (1)은 '='이 2개이며 느슨한 비교이기 때문에 true가 출력되고, (2)는 '='이 3개이며 변수의 자료형까지 다 비교하므로 false가 출력된다. PHP 이외에도 이와 유사한 형태로 비교 연산자를 분리한 언어에서는 이와 유사한 식으로 인식한다. 리스프(Lisp)에서는 데이터 단위가 리스트인지라, 복수인 nil을 사용하며, false의 의미로도 사용한다. 오브젝트-C(Objective-C)에서는 NULL, nil, NIL 모두 기술적으론 똑같이 (void *0) 이긴 하지만 혼용해서는 안 된다. NULL은 고전적인 C 널 포인터, NIL은 클래스 널 포인터, nil은 인스턴스 널 포인터로 서로 역할이 나뉘어져 있다.[5][1]

런타임 환경[편집]

널 프로그램에서 사용할 수 있는 환경은 초기에 비어있는 3개의 큐와 임의의 2개의 양의 정수인 변수 'x'와 'y'로 구성된다. 'x'는 프로그램에 설정되고 'y'는 1로 설정된다.

그런 다음, 'x'를 'x'의 가장 작은 소인수로 나누고 'y'를 곱한 다음 소수 요소에 해당하는 명령어가 실행된다.

명령어[편집]

2 다음 (마지막일 경우 첫 번째) 큐 선택.
3 이전 (첫 번째일 경우 마지막) 큐 선택.
5 선택한 큐의 앞에 있는 바이트를 (큐가 비어있으면 NUL을) 출력한다. 이 때 NUL은 '\0' 이다(아스키 코드 0).
7 한 바이트를 입력받고 선택한 큐의 앞의 값을 덮어쓰며, 선택한 큐가 비어 있다면 입력받은 값을 큐에 추가한다. 현재 EOF를 입력받았을 때의 동작에 대해서는 정의되어 있지 않다.
11 선택한 큐의 앞에 있는 바이트를 (큐가 비어있다면 0을) y에 뺀다. 만약 y가 음수가 되면 y를 0으로 설정한다.
13 선택한 큐의 앞에 있는 바이트를 (큐가 비어있다면 0을) y에 더한다.
17 선택한 큐의 앞에 y를 256으로 나눈 값을 집어넣는다.
19 선택한 큐의 앞에 있는 바이트를 (선택한 큐가 비어있을 경우에는 0을) 꺼내서 다음 (마지막일 경우 첫 번째) 큐의 뒤에 추가한다.
23 선택한 큐의 앞에 있는 바이트를 (선택한 큐가 비어있을 경우에는 0을) 꺼내서 이전 (첫 번째일 경우 마지막) 큐의 뒤에 추가한다.
29 선택한 큐의 앞에 있는 바이트를 제거한다.
31 선택한 큐의 끝에 y를 256으로 나눈 값을 추가한다.
37 만약 선택한 큐가 비어있거나 선택한 큐의 앞의 바이트 값이 0이면 x를 가장 작은 소인수로 나누고 y에 곱한다.
41 두 변수(x, y)의 값을 서로 변경.
43 프로그램 종료

이 명령어는 항상 14번째 소수마다 반복된다. 예를 들어, 47은 2와 같은 역할을 한다.[6]

구분[편집]

널 포인터[편집]

널 포인터(null pointer)는 유효한 객체를 참조하지 않는 포인터를 나타내기 위한 예약된 값을 갖는다. 프로그램들은 일상적으로 알려지지 않은 길이를 가진 리스트의 끝이나 특정한 행동 수행 실패 같은 조건들을 표현하기 위해 널 포인터를 사용한다. 널 포인터들의 이러한 사용은 널 입력 가능(Nullabe) 타입들, 옵션 타입에서의 없는 값과 비교될 수 있다. 반드시 초기화되지 않은 포인터와 혼동되어서는 안된다. 널 포인터는 유효한 객체를 가리키는 어느 포인터와 비교해도 같지 않게 보장된다. 그러나 언어와 구현에 따라서, 초기화되지 않은 포인터는 이것을 보장하지 않는다. 초기화되지 않은 포인터 다른 유효한 포인터들 또는 널 포인터들과 비교해서 같을 수 있다. 즉, 초기화되지 않은 포인터는 때에 따라서 두 경우 모두가 될 수 있다. 널 포인터와 널 값은 다른 의미를 갖는다. 대부분의 프로그래밍 언어에서 널 포인터는 "값없음"을 의미하지만, 관계형 데이터베이스에서 널 값은 "알려지지 않은 값"을 의미한다. 이것은 실질적으로 중요한 차이로 이끌어 진다. 대부분의 프로그래밍 언어들은 두 널 포인터를 같다고 여기지만, 관계형 데이터베이스 엔진은 알려지지 않은 값들을 의미해서, 같은지도 알려져 있지 않으므로 두 널 값을 같다고 여기지 않는다.[7]

3가 논리(3VL)[편집]

널은 데이터 도메인의 한 부분이 아니기 때문에 값으로 여기 지지는 않지만, 값의 부재를 표시하는 표시자로서의 역할을 한다. 이러한 이유로 인해 널과 비교를 하는 것은 ‘참’도 ‘거짓’도 아니며 항상 제3의 값, ‘미지’(unknown)로 나타난다. 10과 널을 비교하는 아래 표현의 논리적 결과는 미지이다.

SELECT 10 = NULL       -- 결과는 Unknown

그러나, 만약 널 값이 연산의 결과물에 적합하지 않을 때 널에 대한 어떤 연산은 값을 리턴할 수 있다.

SELECT NULL OR TRUE   -- 결과는 참

이 경우, OR의 왼쪽에 있는 값이 알 수 없다는 사실이 부적절하다. 왜냐하면, OR 연산의 결과가 왼쪽 값에 상관없이 참일 것이기 때문이다.[2]

0과 \0[편집]

널은 헤더 파일에 정의된 매크로로 널 포인터 상수(null pointer constant)이다. 컴파일러에 의해 (void*)0 으로 정의된다. 일반적으로 C언어에서 stdio.h 파일, C++에서 iostream 헤더 파일을 include 시에 사용할 수 있다. 널은 0 주소를 의미하기 때문에 포인터 변수를 초기화 시에 사용한다.

char *ptr = NULL 과  char *ptr = 0   //같은 의미
int a = NULL 과 int a = 0   //다른 의미

왜냐하면 전자의 경우, 널의 의미는 a 변수에 주솟값 0을 넣는 것이고 후자는 정수 0을 a 변수에 넣는다는 뜻이다. 즉, 널은 0이랑 의미가 거의 같다. 포인터를 초기화 시에 0은 0 주소라는 것을 알고 있다는 가정하에 쓰기 때문에 널과 0의 의미를 동일하게 보는 것이다. 정수형 변수를 초기화 시에 널을 쓰는 일은 없기 때문이다. 문자열을 표현할 때는 문자열의 끝을 의미하는 문자인 "\0"이 삽입된다. 이 문자를 가리켜 널 문자라 하며 아스키코드 값 0에 해당한다. 심볼(sysbol(name))은 NUL이다. 숫자 0과 구분해 주기 위해 "\0"으로 사용한다. 문자 0은 아스키코드값 0x20이기 때문에 다르다.[8]

각주[편집]

참고자료[편집]

같이 보기[편집]


  검수요청.png검수요청.png 이 널 문서는 프로그래밍에 관한 글로서 검토가 필요합니다. 위키 문서는 누구든지 자유롭게 편집할 수 있습니다. [편집]을 눌러 문서 내용을 검토·수정해 주세요.