복합 기본 키의 Null 허용 열에 어떤 문제가 있습니까?


149

ORACLE은 기본 키를 구성하는 열에서 NULL 값을 허용하지 않습니다. 대부분의 다른 "엔터프라이즈 수준"시스템에서도 마찬가지입니다.

동시에, 대부분의 시스템은 널 입력 가능 컬럼에 고유 한 제한 조건 을 허용합니다.

고유 제한 조건에 NULL이있을 수 있지만 기본 키가없는 이유는 무엇입니까? 이것에 대한 근본적인 논리적 이유가 있습니까, 아니면 이것이 기술적 한계가 있습니까?


답변:


216

기본 키는 행을 고유하게 식별하기위한 것입니다. 이것은 키의 모든 부분을 입력과 비교하여 수행됩니다.

정의에 따라 NULL은 성공적인 비교의 일부가 될 수 없습니다. 자체 비교 ( NULL = NULL)도 실패합니다. 이는 NULL을 포함하는 키가 작동하지 않음을 의미합니다.

또한 외래 키에서는 NULL을 사용하여 선택적 관계를 표시 할 수 있습니다. (*) PK에서도 허용하면이 문제가 발생합니다.


(*) 주의 사항 : 널 입력 가능 외래 키를 갖는 것은 관계형 데이터베이스 설계가 깨끗하지 않습니다.

이 두 개의 엔티티 인 경우 AB위치를 A선택적으로 관련 될 수있다 B, 깨끗한 솔루션은 해상도 테이블을 만드는 것입니다 (의 말을하자 AB). 그 표는 연결 것 A으로 B:이 경우 입니다 관계가 있다면 그것은, 기록을 포함하는 것 아니다 는 않을 것 다음.


5
수락 된 답변을 이것으로 변경했습니다. 투표로 판단하면이 답변은 더 많은 사람들에게 가장 분명합니다. Tony Andrews의 답변 이이 디자인 의 의도를 더 잘 설명하고 있다고 생각합니다 . 그것도 확인하십시오!
Roman Starkov

2
Q : 행이없는 대신 NULL FK를 언제 원합니까? A : 최적화를 위해 비정규 화 된 스키마 버전에서만. 사소한 스키마에서는 이와 같이 표준화되지 않은 문제는 새로운 기능이 필요할 때마다 문제를 일으킬 수 있습니다. 웹 디자인 군중은 신경 쓰지 않습니다. 나는 이것이 좋은 디자인 아이디어처럼 들리는 대신 이것에 대해주의를 기울여야합니다.
zxq9

3
"널링 가능한 외래 키가 있다고해서 관계형 데이터베이스 디자인이 깨끗하지는 않습니다." -null이없는 데이터베이스 디자인 (6 번째 정규 형식)은 복잡성을 항상 증가 시키며, 공간 절약 효과는 이러한 이점을 실현하는 데 필요한 추가 프로그래머 작업으로 인해 종종 중요합니다.
다이

1
ABC 해상도 테이블이면 어떻게 되나요? 선택 사양 C
바트 Calixto

1
나는 "표준이 그것을 금지하기 때문에"라고 쓰지 않으려 고 노력했다. 이것은 실제로 아무것도 설명하지 않기 때문이다.
Tomalak

62

기본 키는 테이블의 모든 행에 대한 고유 식별자를 정의 합니다. 테이블에 기본 키가 있으면 원하는 행을 선택할 수 있습니다.

고유 제한 조건이 반드시 모든 행을 식별하지는 않습니다. 그냥하도록 지정 하면 행이 컬럼의 값을 가지고, 다음, 그들이 고유해야합니다. 이것은 모든 행 을 고유하게 식별하기에는 충분하지 않으므로 기본 키가 수행해야합니다.


10
Sql Server에서 Null을 허용하는 열이있는 고유 제약 조건은 해당 열의 'null'값을 한 번만 허용합니다 (제약 조건의 다른 열에 대해 동일한 값이 제공됨). 따라서 이러한 고유 제한 조건은 기본적으로 널 입력 가능 열이있는 pk처럼 작동합니다.
Gerard

나는 (11.2) 오라클에 대해 동일한 확인
알렉산더 Malakhov

2
Oracle (SQL Server에 대해 모른다)에서 테이블에는 고유 제약 조건의 모든 열이 null 인 많은 행이 포함될 수 있습니다 . 그러나 고유 제한 조건의 일부 열이 널이 아니고 일부가 널인 경우 고유성이 적용됩니다.
Tony Andrews

이것이 복합 UNIQUE에 어떻게 적용됩니까?
Dim

1
@Dims SQL 데이터베이스의 다른 거의 모든 것과 마찬가지로 "구현에 달려 있습니다". 대부분의 db에서 "기본 키"는 실제로 고유 한 제약 조건입니다. "기본 키"라는 개념은 UNIQUE의 개념보다 더 특별하거나 강력하지 않습니다. 실제 차이점은 UNIQUE를 보장 할 수있는 테이블에 대해 두 개의 독립적 인 측면이있는 경우 정의에 따라 정규화 된 데이터베이스가없는 것입니다 (동일한 테이블에 두 가지 유형의 데이터를 저장함).
zxq9 2016 년

46

기본적으로 다중 열 기본 키의 NULL에는 아무런 문제가 없습니다. 그러나 디자이너가 의도하지 않은 의미를 갖는 것은 많은 시스템이 오류를 발생시키는 이유입니다.

일련의 필드로 저장된 모듈 / 패키지 버전의 경우를 고려하십시오.

CREATE TABLE module
  (name        varchar(20) PRIMARY KEY,
   description text DEFAULT '' NOT NULL);

CREATE TABLE version
  (module      varchar(20) REFERENCES module,
   major       integer NOT NULL,
   minor       integer DEFAULT 0 NOT NULL,
   patch       integer DEFAULT 0 NOT NULL,
   release     integer DEFAULT 1 NOT NULL,
   ext         varchar(20),
   notes       text DEFAULT '' NOT NULL,
   PRIMARY KEY (module, major, minor, patch, release, ext));

기본 키의 처음 5 개 요소는 정기적으로 릴리스 버전의 일부로 정의되지만 일부 패키지는 일반적으로 정수가 아닌 사용자 정의 된 확장명 (예 : "rc-foo"또는 "vanilla"또는 "beta"또는 기타 다른 것)을 갖습니다. 누구 네 개의 필드은) 꿈을 꿀 수도 부족하다. 패키지에 확장명이 없으면 위의 모델에서 NULL이며, 그렇게 놔두면 아무런 해가 없습니다.

그러나 NULL 무엇 입니까? 그것은 정보 의 부족 , 알 수없는 것으로 나타납니다 . 즉, 아마도 이것이 더 의미가 있습니다.

CREATE TABLE version
  (module      varchar(20) REFERENCES module,
   major       integer NOT NULL,
   minor       integer DEFAULT 0 NOT NULL,
   patch       integer DEFAULT 0 NOT NULL,
   release     integer DEFAULT 1 NOT NULL,
   ext         varchar(20) DEFAULT '' NOT NULL,
   notes       text DEFAULT '' NOT NULL,
   PRIMARY KEY (module, major, minor, patch, release, ext));

이 버전에서 튜플의 "ext"부분은 NULL이 아니지만 기본값은 비어있는 문자열입니다. 이는 의미 상 (실제로는) NULL과 다릅니다. 빈 문자열은 "무엇이 존재하지 않는지"고의로 기록한 반면 NULL은 알 수 없습니다. 다시 말해, "빈"과 "널"은 다른 것입니다. "여기에 가치가 없습니다"와 "여기에 가치가 무엇인지 모르겠습니다."의 차이점이 있습니다.

버전 확장이없는 패키지를 등록하면 확장이 없다는 것을 알고 있으므로 빈 문자열이 실제로 올바른 값입니다. NULL은 확장명이 있는지 여부를 모르거나 그것이 무엇인지 알았지 만 모르는 경우에만 정확합니다. 문자열 값이 표준 인 시스템에서는이 상황을 다루기가 더 쉽습니다. 0 또는 1을 삽입하는 것 이외의 "빈 정수"를 나타내는 방법이 없기 때문에 나중에 비교할 때 롤업됩니다. 자체 의미) *.

우연히도 Postgres에서는 두 가지 방법이 모두 유효하지만 ( "엔터프라이즈"RDMBS에 대해 논의하고 있기 때문에) NULL을 믹스에 넣을 때 비교 결과가 약간 다를 수 있습니다. 알려지지 않은 것을 알 수 없으므로 NULL을 포함하는 비교 결과가 NULL입니다. 위험! 이것에 대해 신중하게 생각하십시오. 이것은 NULL 비교 결과 가 일련의 비교를 통해 전파됨 을 의미합니다 . 이것은 정렬, 비교 등의 미묘한 버그의 원인이 될 수 있습니다.

Postgres는 귀하가 성인이라고 가정하고 스스로 결정할 수 있습니다. Oracle과 DB2는 당신이 어리석은 짓을하고 있다는 것을 몰랐으며 오류를 던졌다 고 가정합니다. 이것은 일반적 으로 올바른 일이지만 항상 그런 것은 아닙니다 . 어떤 경우에는 실제로 알지 못하고 NULL을 가질 수 있으므로 의미있는 비교가 불가능한 알 수없는 요소가있는 행을 남기는 것이 올바른 동작입니다.

어쨌든 전체 스키마에서 허용하는 NULL 필드 수를 제거하고 기본 키의 일부인 필드에 대해서는 이중으로 제거해야합니다. 대부분의 경우 NULL 열의 존재는 정규화되지 않은 (고의적으로 비정규 화되지 않은) 스키마 설계를 나타내며 허용되기 전에 매우 열심히 생각해야합니다.

[* 참고 : 정수와 "알 수 없음"과 의미 적으로 "빈"을 의미하는 "하단"형식의 조합 인 사용자 지정 형식을 만들 수 있습니다. 불행히도 이것은 비교 연산에 약간의 복잡성을 초래하며 일반적으로 실제로 유형이 정확하다는 것은 실제로 많은 노력을 기울일 가치가 없으므로 NULL처음에는 많은 가치를 허용해서는 안됩니다 . 즉, RDBMS 에 "값 없음"의 의미를 "알 수없는 값"으로 자연스럽게 병합하는 습관을 방지하는 BOTTOM것 외에도 기본 유형을 포함하는 것이 좋을 것 NULL입니다. ]


5
이것은 매우 좋은 답변이며 NULL 값에 대해 많이 설명하며 많은 상황에서 영향을 미칩니다. 당신은 지금 내 존경을 가지고 있습니다! 대학조차도 데이터베이스 내부의 NULL 값에 대해 좋은 설명을 얻었습니다. 감사합니다!

이 답변의 주요 아이디어를지지합니다. 그러나 '정보의 부족, 미지의', '의미 적으로 (실제적으로) NULL과 다르다', 'A의 NULL은 미지의', '빈 문자열은'존재하지 않는 것 '에 대한 고의적 인 기록 " ','NULL =="모르다 ""등은 모호하고 오해의 소지가 있으며 실제로는 NULL 또는 어떤 값이 사용되는지 또는 사용 하려는지에 대한 결석 문에 대한 니모닉입니다. . (SQL NULL 기능의 (나쁜) 디자인에 영감을주는 것을 포함하여) 아무것도 정당화하거나 설명하지 않습니다. 그것들은 설명되고 디 unk 크되어야한다.
philipxy

21

NULL == NULL-> false (적어도 DBMS에서는)

따라서 실제 값이있는 추가 열이있는 경우에도 NULL 값을 사용하여 관계를 검색 할 수 없습니다.


1
이것은 가장 좋은 대답처럼 들리지만 기본 키 생성시 왜 이것이 금지되는지 이해하지 못합니다. 이것이 단지 검색 문제인 경우 where pk_1 = 'a' and pk_2 = 'b'에는 정상 값을 사용 하고 where pk_1 is null and pk_2 = 'b'널이있을 때 전환 할 수 있습니다 .
EoghanM

또는 더 안정적으로, where (a.pk1 = b.pk1 or (a.pk1 is null and b.pk1 is null)) and (a.pk2 = b.pk2 or (a.pk2 is null and b.pk2 is null))/
Jordan Rieger

8
잘못된 답변. NULL == NULL-> 알 수 없음 거짓이 아닙니다. 테스트 결과가 알 수없는 경우 제약 조건을 위반 한 것으로 간주되지 않습니다. 이것은 종종 비교가 거짓을 만드는 것처럼 SEEM 으로 만들지 만 실제로는 그렇지 않습니다.
Erwin Smout

4

Tony Andrews의 답변은 괜찮습니다. 그러나 진정한 대답은 이것이 관계형 데이터베이스 커뮤니티에서 사용하는 규칙이었으며 필수는 아니라는 것입니다. 어쩌면 좋은 컨벤션 일 수도 있고 아닐 수도 있습니다.

NULL과 비교하면 UNKNOWN (3 진값)이됩니다. 따라서 null로 제안 된 것처럼 평등에 관한 모든 전통적 지혜가 창 밖으로 나옵니다. 그것이 언뜻보기에 그렇게 보입니다.

그러나 이것이 반드시 그렇게 생각하지는 않으며 SQL 데이터베이스조차도 NULL이 비교의 모든 가능성을 파괴한다고 생각하지 않습니다.

데이터베이스에서 SELECT * FROM VALUES (NULL) 쿼리를 실행하십시오. UNION SELECT * FROM VALUES (NULL)

당신이 보는 것은 NULL 값을 가진 하나의 속성을 가진 하나의 튜플입니다. 따라서 유니온은 여기서 두 개의 NULL 값을 동일한 것으로 인식했습니다.

3 개의 구성 요소가있는 복합 키를 3 개의 속성 (1, 3, NULL) = (1, 3, NULL)을 갖는 튜플과 비교할 때 <=> 1 = 1 AND 3 = 3 AND NULL = NULL이 결과는 알 수 없음 .

그러나 우리는 새로운 종류의 비교 연산자를 정의 할 수 있습니다. ==. X == Y <=> X = Y OR (X IS NULL AND Y IS NULL)

이러한 종류의 항등 연산자가 있으면 null 구성 요소가있는 복합 키 또는 null 값이있는 비 복합 키를 문제가되지 않습니다.


1
아니요, UNION은 두 개의 NULL을 명확하지 않은 것으로 인식했습니다. "같음"과 같은 것은 아닙니다. 대신 UNION ALL을 시도하면 두 개의 행이 나타납니다. "새로운 종류의 비교 연산자"는 이미 SQL에 있습니다. 구별되지 않습니다. 그러나 그 자체로는 충분하지 않습니다. NATURAL JOIN 또는 외래 키의 REFERENCES 절과 같은 SQL 구문에서 이것을 사용하려면 해당 구문에 대한 추가 옵션이 필요합니다.
Erwin Smout

어윈 스 머트 이 포럼에서도 만나 meet 게되어 반갑습니다. SQL의 "IS NOT DISTINCT FROM"을 알지 못했습니다. 매우 흥미로운! 그러나 그것이 내가 만든 == 연산자로 정확히 의미하는 것 같습니다. 왜 "그 자체로는 충분하지 않다"고 말하는 이유를 설명해 주시겠습니까?
Rami Ojares

REFERENCES 절은 정의에 따라 동등성을 기반으로합니다. (더 엄격한) EQUAL 대신 NOT DISTINCT 인 해당 속성 값을 기반으로 부모 튜플 / 행과 자식 튜플 / 행을 일치시키는 일종의 참조는이 옵션을 지정하는 기능이 필요하지만 구문은 그렇지 않습니다. 허용하십시오. 자연 가입을위한 Ditto.
Erwin Smout

외래 키가 작동하려면 참조가 고유해야합니다 (즉, 모든 값이 고유해야 함). 이는 단일 null 값을 가질 수 있음을 의미합니다. NOT DISTINCT 연산자로 REFERENCES를 정의한 경우 모든 널값은 해당 단일 널을 참조 할 수 있습니다. 나는 그것이 더 유용하다는 점에서 더 좋을 것이라고 생각합니다. JOIN (외부 및 내부 모두)을 사용하면 왼쪽의 null이 오른쪽의 모든 null과 일치 할 때 "NULL MATCHES"가 곱해지기 때문에 엄격한 같음이 더 좋습니다.
Rami Ojares

1

나는 이것이 이것이 기술에 의해 야기 된 근본적 / 기능적 결함이라고 믿는다. 고객을 식별 할 수있는 선택적 필드가있는 경우 이제 NULL! = NULL이기 때문에 더미 값을 해킹해야합니다. 특히 우아하지는 않지만 "산업 표준"입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.