고려중인 비즈니스 환경에 대한 설명에 따르면, 항목 (수퍼 타입)과 각 범주 (예 : Car , Boat and Plane) (알 수없는 두 개 이상)를 포함 하는 수퍼 타입 하위 유형 구조가 있습니다. 하위 유형 —.
이러한 시나리오를 관리하기 위해 수행 할 방법을 아래에서 자세히 설명하겠습니다.
비즈니스 규칙
관련 개념 스키마를 설명 하기 위해 지금까지 결정된 가장 중요한 비즈니스 규칙 중 일부 (분석을 세 가지 범주 로만 제한하고 가능한 한 간략하게 유지)는 다음과 같이 공식화 할 수 있습니다.
- 사용자가 제로 일 또는 일대 다 소유 한 항목
- 항목은 정확히-하나가 소유하고 사용자 의 특정 순간에
- 항목 일대에 의해 소유 될 수 있습니다 사용자 시간에 서로 다른 지점에서
- 항목은 정확히 하나 개에 의해 분류 카테고리
- 항목 , 항상이다
예시적인 IDEF1X 다이어그램
그림 1 은 이전 공식을 그룹화하기 위해 생성 한 IDEF1X 1 다이어그램과 관련이있는 다른 비즈니스 규칙을 보여줍니다.
수퍼 타입
한편,에 항목 , 상위 유형, 선물 속성 † 모두에 공통 또는 속성 카테고리 , 즉,
- CategoryCode은 외국 키 (FK)를 참조과 -specified Category.CategoryCode 서브 타입 등 및 기능을 판별 즉, 그것은 정확한 표시 카테고리 주어진있는 하위 유형의 항목 connected-이어야합니다,
- OwnerId —User.UserId를 가리키는 FK로 구분되었지만 , 특별한 의미를보다 정확하게 반영하기 위해 역할 이름 2 를 할당했습니다.
- 푸 ,
- 바 ,
- 바즈 와
- CreatedDateTime 입니다.
하위 유형
한편, 속성 ‡ 모든 특정에 해당하는 것으로 분류 , 즉,
- Qux 와 Corge ;
- Grault , Garply 및 Plugh ;
- Xyzzy , Thud , Wibble 및 Flob ;
해당 하위 유형 상자에 표시됩니다.
식별자
그런 다음 Item.ItemId PRIMARY KEY (PK)는 역할 이름이 다른 하위 유형으로 3 을 마이그레이션했습니다 .
- CarId ,
- BoatId 및
- PlaneId .
상호 배타적 협회
도시 된 바와 같이, (a) 각각의 수퍼 타입 발생과 (b) 상보적인 서브 타입 인스턴스간에 카디널리티 일대일 (1 : 1) 의 연관 또는 관계가있다 .
전용 하위 유형의 기호가 서브 타입, 즉, 상호 배타적 인 사실을 묘사하고, 구체적인 항목의 발생은 단 하나의 하위 유형 인스턴스에 의해 보충 할 수 있습니다 중 하나 자동차 , 또는 하나의 평면 , 또는 1 보트 (결코 두 이상).
† , ‡ 실제 교단이 질문에 공급되지 않은 것처럼 나는 엔터티 형식의 속성 중 일부 권리를 부여하는 고전적인 자리 표시 자 이름을 고용했다.
설명 논리 레벨 레이아웃
결과적으로 설명 적 논리 설계를 논의하기 위해 위에서 표시되고 설명 된 IDEF1X 다이어그램을 기반으로 다음과 같은 SQL-DDL 문을 도출했습니다.
-- You should determine which are the most fitting
-- data types and sizes for all your table columns
-- depending on your business context characteristics.
-- Also, you should make accurate tests to define the
-- most convenient INDEX strategies based on the exact
-- data manipulation tendencies of your business context.
-- As one would expect, you are free to utilize
-- your preferred (or required) naming conventions.
CREATE TABLE UserProfile (
UserId INT NOT NULL,
FirstName CHAR(30) NOT NULL,
LastName CHAR(30) NOT NULL,
BirthDate DATE NOT NULL,
GenderCode CHAR(3) NOT NULL,
Username CHAR(20) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT UserProfile_PK PRIMARY KEY (UserId),
CONSTRAINT UserProfile_AK1 UNIQUE ( -- Composite ALTERNATE KEY.
FirstName,
LastName,
GenderCode,
BirthDate
),
CONSTRAINT UserProfile_AK2 UNIQUE (Username) -- ALTERNATE KEY.
);
CREATE TABLE Category (
CategoryCode CHAR(1) NOT NULL, -- Meant to contain meaningful, short and stable values, e.g.; 'C' for 'Car'; 'B' for 'Boat'; 'P' for 'Plane'.
Name CHAR(30) NOT NULL,
--
CONSTRAINT Category_PK PRIMARY KEY (CategoryCode),
CONSTRAINT Category_AK UNIQUE (Name) -- ALTERNATE KEY.
);
CREATE TABLE Item ( -- Stands for the supertype.
ItemId INT NOT NULL,
OwnerId INT NOT NULL,
CategoryCode CHAR(1) NOT NULL, -- Denotes the subtype discriminator.
Foo CHAR(30) NOT NULL,
Bar CHAR(30) NOT NULL,
Baz CHAR(30) NOT NULL,
CreatedDateTime DATETIME NOT NULL,
--
CONSTRAINT Item_PK PRIMARY KEY (ItemId),
CONSTRAINT Item_to_Category_FK FOREIGN KEY (CategoryCode)
REFERENCES Category (CategoryCode),
CONSTRAINT Item_to_User_FK FOREIGN KEY (OwnerId)
REFERENCES UserProfile (UserId)
);
CREATE TABLE Car ( -- Represents one of the subtypes.
CarId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Qux CHAR(30) NOT NULL,
Corge CHAR(30) NOT NULL,
--
CONSTRAINT Car_PK PRIMARY KEY (CarId),
CONSTRAINT Car_to_Item_FK FOREIGN KEY (CarId)
REFERENCES Item (ItemId)
);
CREATE TABLE Boat ( -- Stands for one of the subtypes.
BoatId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Grault CHAR(30) NOT NULL,
Garply CHAR(30) NOT NULL,
Plugh CHAR(30) NOT NULL,
--
CONSTRAINT Boat_PK PRIMARY KEY (BoatId),
CONSTRAINT Boat_to_Item_FK FOREIGN KEY (BoatId)
REFERENCES Item (ItemId)
);
CREATE TABLE Plane ( -- Denotes one of the subtypes.
PlaneId INT NOT NULL, -- Must be constrained as (a) the PRIMARY KEY and (b) a FOREIGN KEY.
Xyzzy CHAR(30) NOT NULL,
Thud CHAR(30) NOT NULL,
Wibble CHAR(30) NOT NULL,
Flob CHAR(30) NOT NULL,
--
CONSTRAINT Plane_PK PRIMARY KEY (PlaneId),
CONSTRAINT Plane_to_Item_PK FOREIGN KEY (PlaneId)
REFERENCES Item (ItemId)
);
설명 된 바와 같이, 슈퍼 엔티티 유형 및 각 서브 엔티티 유형은 대응하는 기본 테이블 로 표현된다 .
열 CarId
, BoatId
및 PlaneId
해당 테이블의 PK와 같은 제약, FK 제약로서 개념적 레벨 일대일 관계를 나타내는 도움 § 것을 가리키고 ItemId
의 PK로 구속 컬럼 Item
테이블. 이는 실제 "쌍"에서 상위 유형과 하위 유형 행이 모두 동일한 PK 값으로 식별됨을 나타냅니다. 따라서 언급하는 것이 이상합니다
- (a) 시스템 제어 대리 값을 보유하기 위해 여분의 열을 첨부하는 것 ” ~ (b) 하위 유형을 나타내는 테이블은 (c) 전적으로 불필요한 것 입니다.
§ 주석에서 언급 한 상황 (특히 FOREIGN) KEY 제약 조건 정의와 관련된 문제와 오류를 방지하려면 다음 표에 설명 된대로 현재 사용중인 여러 테이블간에 존재하는 종속성 을 고려해야합니다 . 이 SQL Fiddle 에서도 제공 한 설명 DDL 구조에서 테이블의 선언 순서
″ 예를 들어, AUTO_INCREMENT 속성이있는 추가 열 을 MySQL 기반의 데이터베이스 테이블에 추가합니다.
무결성 및 일관성 고려 사항
비즈니스 환경에서 (1) 각 "슈퍼 타입"행이 항상 해당 "서브 타입"대응 항목으로 보완되는지 확인하고 (2) "subtype"행은 "supertype"행의 "판별 자"열에 포함 된 값과 호환됩니다.
그러한 상황을 선언 적인 방식으로 시행하는 것은 매우 우아 하지만 불행히도 주요 SQL 플랫폼 중 어느 것도 내가 아는 한 그렇게하는 적절한 메커니즘을 제공하지 못했습니다. 따라서 ACID TRANSACTIONS 내에서 절차 코드 를 사용하면 이러한 조건이 항상 데이터베이스에서 충족되도록하는 것이 매우 편리합니다. 다른 옵션은 TRIGGERS를 사용하는 것이지만, 말을하기 어려운 것을 만드는 경향이 있습니다.
유용한 견해 선언
위에서 설명한 것과 같은 논리적 디자인을 가지면 하나 이상의 뷰, 즉 둘 이상의 관련 기본 테이블에 속하는 열을 포함하는 파생 테이블 을 만드는 것이 매우 실용적 입니다. 이러한 방식으로, 예를 들어 "결합 된"정보를 검색 할 때마다 모든 JOIN을 작성하지 않고도 해당 뷰에서 직접 SELECT를 수행 할 수 있습니다.
샘플 데이터
이와 관련하여 기본 테이블이 아래에 표시된 샘플 데이터로 "채워진다"고 가정합니다.
--
INSERT INTO UserProfile
(UserId, FirstName, LastName, BirthDate, GenderCode, Username, CreatedDateTime)
VALUES
(1, 'Edgar', 'Codd', '1923-08-19', 'M', 'ted.codd', CURDATE()),
(2, 'Michelangelo', 'Buonarroti', '1475-03-06', 'M', 'michelangelo', CURDATE()),
(3, 'Diego', 'Velázquez', '1599-06-06', 'M', 'd.velazquez', CURDATE());
INSERT INTO Category
(CategoryCode, Name)
VALUES
('C', 'Car'), ('B', 'Boat'), ('P', 'Plane');
-- 1. ‘Full’ Car INSERTion
-- 1.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(1, 1, 'C', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 1.2
INSERT INTO Car
(CarId, Qux, Corge)
VALUES
(1, 'Fantastic Car', 'Powerful engine pre-update!');
-- 2. ‘Full’ Boat INSERTion
-- 2.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(2, 2, 'B', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 2.2
INSERT INTO Boat
(BoatId, Grault, Garply, Plugh)
VALUES
(2, 'Excellent boat', 'Use it to sail', 'Everyday!');
-- 3 ‘Full’ Plane INSERTion
-- 3.1
INSERT INTO Item
(ItemId, OwnerId, CategoryCode, Foo, Bar, Baz, CreatedDateTime)
VALUES
(3, 3, 'P', 'This datum', 'That datum', 'Other datum', CURDATE());
-- 3.2
INSERT INTO Plane
(PlaneId, Xyzzy, Thud, Wibble, Flob)
VALUES
(3, 'Extraordinary plane', 'Traverses the sky', 'Free', 'Like a bird!');
--
그런 다음, 유리한보기 하나에서 집결 열은 Item
, Car
및 UserProfile
:
--
CREATE VIEW CarAndOwner AS
SELECT C.CarId,
I.Foo,
I.Bar,
I.Baz,
C.Qux,
C.Corge,
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Car C
ON C.CarId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
물론, 비슷한 접근 방식은 당신이뿐만 아니라 "전체"선택할 수 있도록 올 수 있습니다 Boat
및 Plane
정보 똑바로 하나 개의 테이블 (a는 이러한 경우에, 하나의 파생).
당신 -if 그 다음 VIEW 정의와 결과 세트 -에서 NULL 마크의 존재에 대해 신경 쓰지 않는 한, 당신이 할 수있는, 예를 들어, "수집"테이블의 열 Item
, Car
, Boat
, Plane
및 UserProfile
:
--
CREATE VIEW FullItemAndOwner AS
SELECT I.ItemId,
I.Foo, -- Common to all Categories.
I.Bar, -- Common to all Categories.
I.Baz, -- Common to all Categories.
IC.Name AS Category,
C.Qux, -- Applies to Cars only.
C.Corge, -- Applies to Cars only.
--
B.Grault, -- Applies to Boats only.
B.Garply, -- Applies to Boats only.
B.Plugh, -- Applies to Boats only.
--
P.Xyzzy, -- Applies to Planes only.
P.Thud, -- Applies to Planes only.
P.Wibble, -- Applies to Planes only.
P.Flob, -- Applies to Planes only.
U.FirstName AS OwnerFirstName,
U.LastName AS OwnerLastName
FROM Item I
JOIN Category IC
ON I.CategoryCode = IC.CategoryCode
LEFT JOIN Car C
ON C.CarId = I.ItemId
LEFT JOIN Boat B
ON B.BoatId = I.ItemId
LEFT JOIN Plane P
ON P.PlaneId = I.ItemId
JOIN UserProfile U
ON U.UserId = I.OwnerId;
--
여기에 표시된 뷰의 코드는 단지 예시 일뿐입니다. 물론 몇 가지 테스트 연습과 수정을 수행하면 현재 쿼리를 (물리적) 실행하는 데 도움이 될 수 있습니다. 또한 비즈니스 요구에 따라 해당 뷰에서 열을 제거하거나 추가해야 할 수도 있습니다.
샘플 데이터와 모든 뷰 정의 가이 SQL Fiddle에 통합되어 "작동 중"으로 관찰 될 수 있습니다.
데이터 조작 : 응용 프로그램 코드 및 열 별명
응용 프로그램 코드 (“서버 측 특정 코드”의 의미 인 경우) 및 열 별칭의 사용은 다음 주석에서 제기 한 다른 중요한 사항입니다.
서버 측 특정 코드에 대한 해결 방법 [조인] 문제를 해결했지만 실제로는 그렇게하고 싶지 않습니다. 모든 열에 별칭을 추가하면 "스트레스"가 될 수 있습니다.
잘 설명했습니다. 대단히 감사합니다. 그러나 의심 한 것처럼 일부 열과의 유사성으로 인해 모든 데이터를 나열 할 때 결과 집합을 조작해야합니다. 여러 별칭을 사용하여 명령문을 깨끗하게 유지하고 싶지 않기 때문입니다.
애플리케이션 프로그램 코드를 사용하는 것은 결과 세트의 프리젠 테이션 (또는 그래픽) 기능을 처리하는 데 매우 적합한 자원이지만 행 단위로 데이터 검색을 피하는 것이 실행 속도 문제를 방지하는 데 가장 중요하다는 것을 나타내는 것이 좋습니다. 시스템의 동작을 최적화 할 수 있도록 SQL 플랫폼의 (정확하게) 설정된 엔진이 제공하는 강력한 데이터 조작 도구를 사용하여 관련 데이터 세트를 가져 오는 것이 목표입니다.
또한 별명을 사용하여 특정 범위 내에서 하나 이상의 열 이름을 바꾸는 것은 스트레스로 보일 수 있지만 개인적으로 (i) 상황화 및 (ii) 관련 의미 와 의도를 명확하게하는 데 도움이되는 매우 강력한 도구라고 생각합니다. 열; 그러므로, 이것은 관심있는 데이터의 조작과 관련하여 철저히 숙고해야 할 측면입니다.
비슷한 시나리오
상호 배타적 인 하위 유형과의 수퍼 유형 하위 유형 연결을 포함하는 다른 두 가지 경우에 대한 내 일련의 게시물 및 이 게시물 그룹에 대한 도움을 얻을 수도 있습니다 .
또한 하위 유형 이이 (최신) 답변 에서 상호 배타적이지 않은 수퍼 유형 하위 유형 클러스터와 관련된 비즈니스 환경에 대한 솔루션을 제안했습니다 .
미주
1 정보 모델링을위한 통합 정의 ( IDEF1X )는1993 년 12 월 미국 표준 기술 연구소 (NIST)에서 표준 으로 확립 한 권장 데이터 모델링 기술입니다. 그것은 단단하게 이론적 인 작품의 일부는 저술의 (a)를 기반으로 유일한 발신자 의 관계형 모델 즉, 박사 EF 커드 ; (b) PP Chen 박사에 의해 개발 된 실체 관계 관점 ; 또한 (c) Robert G. Brown이 만든 논리적 데이터베이스 디자인 기법.
2 IDEF1X에서 역할 이름 은 해당 엔터티 유형의 범위 내에서 유지되는 의미를 표현하기 위해 FK 속성 (또는 특성)에 할당 된 고유 레이블입니다.
3 IDEF1X 표준은 키 마이그레이션 을 "부모 또는 일반 엔터티의 기본 키를 자식 또는 범주 엔터티에 외래 키로 배치하는 모델링 프로세스"로정의합니다.
Item
표에CategoryCode
열이 포함되어 있습니다. “무결성 및 일관성 고려 사항”섹션에서 언급 한 바와 같이