수퍼 타입 / 서브 타입
수퍼 타입 / 서브 타입 패턴을 살펴 보는 것은 어떻습니까? 공통 열은 상위 테이블로 이동합니다. 각 고유 한 유형에는 고유 한 PK로 상위 ID가있는 고유 한 테이블이 있으며 모든 하위 유형에 공통적이지 않은 고유 한 열이 있습니다. 각 장치가 하나 이상의 하위 유형이 될 수 없도록 상위 및 하위 테이블 모두에 유형 열을 포함시킬 수 있습니다. (ItemID, ItemTypeID)에서 자식과 부모 사이에 FK를 만듭니다. FK를 사용하여 다른 유형의 원하는 무결성을 유지하기 위해 수퍼 타입 또는 서브 타입 테이블에 사용할 수 있습니다. 예를 들어, 모든 유형의 ItemID가 허용되는 경우 FK를 상위 테이블에 작성하십시오. SubItemType1 만 참조 할 수있는 경우 해당 테이블에 FK를 작성하십시오. TypeID를 참조 테이블에서 제외합니다.
명명
명명에 관해서는, 내가 볼 때 두 가지 선택이 있습니다 (단지 "ID"의 세 번째 선택은 내 마음 속에 강한 반 패턴이기 때문에). 부모 테이블에있는 것처럼 하위 유형 키 ItemID를 호출하거나 DoohickeyID와 같은 하위 유형 이름을 호출하십시오. 이것에 대해 몇 가지 생각과 경험을 한 후에 나는 그것을 DoohickeyID라고 부릅니다. 그 이유는 실제로 Doohickeys가 아닌 Items를 포함하는 변장에있는 하위 유형 테이블에 대한 혼동이있을 수 있지만 Doohickey 테이블에 FK를 만들 때와 비교할 때 작은 부정적이며 열 이름이 그렇지 않기 때문입니다. 시합!
EAV로 또는 EAV로-EAV 데이터베이스에 대한 나의 경험
EAV가 진정으로해야 할 일이라면해야 할 일입니다. 하지만 당신이해야 할 일이 아니라면 어떻게해야합니까?
비즈니스에서 사용중인 EAV 데이터베이스를 구축했습니다. 감사합니다. 데이터 세트가 작으므로 (수십 개의 항목 유형이 있지만) 성능이 나쁘지 않습니다. 그러나 데이터베이스에 수천 개가 넘는 항목이 있으면 좋지 않습니다! 또한 테이블은 쿼리하기가 너무 어렵습니다. 이 경험을 통해 향후 가능한 EAV 데이터베이스를 피하고 싶었습니다.
이제 내 데이터베이스에서 존재하는 각 하위 유형마다 PIVOTed보기를 자동으로 작성하는 저장 프로 시저를 작성했습니다. AutoDoohickey에서 쿼리 할 수 있습니다. 하위 유형에 대한 메타 데이터에는보기 이름에 사용하기에 적합한 객체 안전 이름이 포함 된 "ShortName"열이 있습니다. 심지어 뷰를 업데이트 할 수있게 만들었습니다! 불행히도 조인에서 업데이트 할 수는 없지만 이미 존재하는 행을 삽입하면 UPDATE로 변환됩니다. 불행히도 INSERT-to-UPDATE 변환 프로세스로 업데이트 할 열을 VIEW에 표시 할 수있는 방법이 없기 때문에 몇 개의 열만 업데이트 할 수 없습니다. NULL 값은 "이 열을 NULL로 업데이트"처럼 보입니다. "이 열을 전혀 업데이트하지 마십시오"를 표시하려고했습니다.
EAV 데이터베이스를보다 쉽게 사용할 수 있도록하기위한 이러한 모든 장식에도 불구하고, 나는이 뷰를 SLOW이기 때문에 대부분의 일반적인 쿼리에서는 사용하지 않습니다. 쿼리 조건은 술어가 Value
테이블로 다시 푸시되지 않으므로 필터링하기 전에 해당 뷰 유형의 모든 항목에 대한 중간 결과 세트를 빌드해야합니다. 아야. 그래서 많은 조인을 가진 많은 쿼리가 있으며 각 쿼리마다 다른 값을 얻습니다. 그들은 비교적 잘 수행하지만 아야! 다음은 예입니다. 이것을 생성하는 SP (그리고 그 업데이트 트리거)는 하나의 거대한 짐승입니다. 나는 그것을 자랑스럽게 생각하지만, 당신이 지금까지 유지하려고하는 것은 아닙니다.
CREATE VIEW [dbo].[AutoModule]
AS
--This view is automatically generated by the stored procedure AutoViewCreate
SELECT
ElementID,
ElementTypeID,
Convert(nvarchar(160), [3]) [FullName],
Convert(nvarchar(1024), [435]) [Descr],
Convert(nvarchar(255), [439]) [Comment],
Convert(bit, [438]) [MissionCritical],
Convert(int, [464]) [SupportGroup],
Convert(int, [461]) [SupportHours],
Convert(nvarchar(40), [4]) [Ver],
Convert(bit, [28744]) [UsesJava],
Convert(nvarchar(256), [28745]) [JavaVersions],
Convert(bit, [28746]) [UsesIE],
Convert(nvarchar(256), [28747]) [IEVersions],
Convert(bit, [28748]) [UsesAcrobat],
Convert(nvarchar(256), [28749]) [AcrobatVersions],
Convert(bit, [28794]) [UsesDotNet],
Convert(nvarchar(256), [28795]) [DotNetVersions],
Convert(bit, [512]) [WebApplication],
Convert(nvarchar(10), [433]) [IFAbbrev],
Convert(int, [437]) [DataID],
Convert(nvarchar(1000), [463]) [Notes],
Convert(nvarchar(512), [523]) [DataDescription],
Convert(nvarchar(256), [27991]) [SpecialNote],
Convert(bit, [28932]) [Inactive],
Convert(int, [29992]) [PatchTestedBy]
FROM (
SELECT
E.ElementID + 0 ElementID,
E.ElementTypeID,
V.AttrID,
V.Value
FROM
dbo.Element E
LEFT JOIN dbo.Value V ON E.ElementID = V.ElementID
WHERE
EXISTS (
SELECT *
FROM dbo.LayoutUsage L
WHERE
E.ElementTypeID = L.ElementTypeID
AND L.AttrLayoutID = 7
)
) X
PIVOT (
Max(Value)
FOR AttrID IN ([3], [435], [439], [438], [464], [461], [4], [28744], [28745], [28746], [28747], [28748], [28749], [28794], [28795], [512], [433], [437], [463], [523], [27991], [28932], [29992])
) P;
다음은 특수 메타 데이터에서 다른 저장 프로 시저에 의해 생성 된 다른 유형의 자동 생성보기로, 서로간에 여러 경로를 가질 수있는 항목 간의 관계를 찾는 데 도움이됩니다 (특히 모듈-> 서버, 모듈-> 클러스터-> 서버, 모듈-> DBMS- > 서버, 모듈-> DBMS-> 클러스터-> 서버) :
CREATE VIEW [dbo].[Link_Module_Server]
AS
-- This view is automatically generated by the stored procedure LinkViewCreate
SELECT
ModuleID = A.ElementID,
ServerID = B.ElementID
FROM
Element A
INNER JOIN Element B
ON EXISTS (
SELECT *
FROM
dbo.Element R1
WHERE
A.ElementID = R1.ElementID1
AND B.ElementID = R1.ElementID2
AND R1.ElementTypeID = 38
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 40
AND B.ElementID = R2.ElementID2
AND R2.ElementTypeID = 38
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 38
AND B.ElementID = R2.ElementID2
AND R2.ElementTypeID = 3122
) OR EXISTS (
SELECT *
FROM
dbo.Element R1
INNER JOIN dbo.Element R2 ON R1.ElementID2 = R2.ElementID1
INNER JOIN dbo.Element C2 ON R2.ElementID2 = C2.ElementID
INNER JOIN dbo.Element R3 ON R2.ElementID2 = R3.ElementID1
WHERE
A.ElementID = R1.ElementID1
AND R1.ElementTypeID = 40
AND C2.ElementTypeID = 3080
AND R2.ElementTypeID = 38
AND B.ElementID = R3.ElementID2
AND R3.ElementTypeID = 3122
)
WHERE
A.ElementTypeID = 9
AND B.ElementTypeID = 17
하이브리드 접근법
EAV 데이터베이스의 동적 인 측면을 가져야 만한다면, 그러한 데이터베이스가있는 것처럼 메타 데이터를 만드는 것을 고려할 수 있지만 실제로는 슈퍼 타입 / 서브 타입 디자인 패턴을 사용하는 것이 좋습니다. 예, 새 테이블을 작성하고 열을 추가, 제거 및 수정해야합니다. 그러나 적절한 사전 처리 (EAV 데이터베이스의 자동보기에서했던 것처럼)를 사용하면 실제 테이블과 같은 객체를 사용할 수 있습니다. 단지, 그들은 나만큼 나쁘지 않았으며 쿼리 최적화 프로그램은 기본 테이블로 푸시 다운 할 수 있습니다 (읽기 : 성능이 우수함). 수퍼 타입 테이블과 서브 타입 테이블 사이에는 하나의 조인 만있을 것입니다. 응용 프로그램은 메타 데이터를 읽도록 설정되어 수행 할 작업을 찾거나 자동 생성보기를 사용할 수 있습니다.
또는 여러 수준의 하위 유형 집합이있는 경우 몇 개의 조인 만 있습니다. 다단계 I는 일부 하위 유형이 공통 열을 공유하지만 모두는 공유하지 않는 경우를 의미하며 자체적으로 몇 가지 다른 테이블의 상위 유형 인 하위 유형 테이블을 가질 수 있습니다. 예를 들어 서버, 라우터 및 프린터에 대한 정보를 저장하는 경우 "IP 장치"의 중간 하위 유형이 적합 할 수 있습니다.
나는 아직 실제 세계에서 시도해 보라고 제안한 것처럼 하이브리드 하이브리드 타입 / 서브 타입 EAV로 변환 가능한 데코 레이팅 된 데이터베이스를 아직 만들지 않았다는 경고를하겠다. 그러나 EAV에서 겪었던 문제는 작지 않으며 데이터베이스가 커지고 값 비싼 거대한 하드웨어없이 좋은 성능을 원한다면 무언가를 하는 것이 절대적으로 필요 합니다 .
제 생각에는 실제 하위 유형 테이블의 사용 / 생성 / 수정을 자동화하는 데 소요 된 시간이 궁극적으로 가장 좋습니다. 데이터 중심의 유연성에 중점을두면 EAV 사운드가 매우 매력적입니다. 누가 요소 유형에 대한 새로운 속성을 요청할 때 약 18 초 후에 추가 할 수 있고 웹 사이트에서 데이터를 즉시 입력 할 수있는 방법을 좋아 합니다 ). 그러나 여러 가지 방법으로 유연성을 달성 할 수 있습니다! 전처리는 또 다른 방법입니다. 그것은 거의 사람들이 사용하지 않는 강력한 방법이므로 완전히 데이터 중심적이지만 하드 코딩 된 성능의 이점을 제공합니다.
(참고 : 예, 그러한 뷰는 실제로 형식이 지정되어 있으며 PIVOT 뷰에는 실제로 업데이트 트리거가 있습니다. :) 누군가가 길고 복잡한 UPDATE 트리거의 끔찍한 고통스러운 세부 사항에 관심이 있다면 알려 주시면 게시하겠습니다. 샘플입니다.)
그리고 하나 더 아이디어
모든 데이터를 하나의 테이블에 넣습니다. 열에 일반 이름을 지정한 다음 여러 용도로 재사용 / 남용하십시오. 이들에 대한보기를 작성하여 합리적인 이름을 지정하십시오. 적합한 데이터 유형의 사용하지 않는 열을 사용할 수없는 경우 열을 추가하고보기를 업데이트하십시오. 하위 유형 / 슈퍼 유형에 대한 나의 길이에도 불구하고 이것이 가장 좋은 방법 일 수 있습니다.