재고 품목에 다양한 속성이있는 경우 재고 데이터베이스 구조


10

엔터프라이즈 하드웨어 정보를 저장하기 위해 인벤토리 데이터베이스를 구축 중입니다. 데이터베이스가 워크 스테이션, 랩톱, 스위치, 라우터, 휴대폰 등의 범위를 추적하는 장치입니다. 장치 일련 번호를 기본 키로 사용하고 있습니다. 내가 겪고있는 문제는 이러한 장치의 다른 속성이 다양하고 인벤토리 테이블에 다른 장치와 관련이없는 필드를 갖고 싶지 않다는 것입니다. 아래는 데이터베이스 일부의 ERD에 대한 링크입니다 (일부 FK 관계는 표시되지 않음). 예를 들어 워크 스테이션 장치 유형의 장치를 phone 테이블에 넣을 수 없도록 설정하려고합니다. 이를 위해서는 장치 유형 또는 클래스의 유효성을 검사하기 위해 많은 트리거를 사용해야하며 다른 속성을 가진 다른 장치가 추적 될 때마다 새 테이블이 추적됩니다.

ERD1

일련 번호에 매핑 할 수있는 속성 테이블을 설정하려고했지만 장치 유형에 적용되지 않는 속성을 장치에 할당 할 수 있습니다. 예를 들어 누군가 원하는 경우 전화 번호 속성을 워크 스테이션에 할당 할 수 있습니다. . 이 사이트에서 다음 구조를 설명하는 설명을 찾았습니다.

위젯 샘플 ERD

이 구조는 속성이 내가 저장하는 항목에 모두 적용 가능한 경우 효과적입니다. 예를 들어 데이터베이스에 휴대 전화 만 저장하는 경우 속성은 터치 스크린, 트랙 패드, 키보드, 4G, 3G 등이 될 수 있습니다. 이 경우 전화에 모두 적용됩니다. 내 데이터베이스에는 특정 유형의 장치에만 적용되는 hostname, circuitType, phoneNumber와 같은 속성이 있습니다.

주어진 장치 유형에 적용되는 속성 만 해당 유형의 장치에 할당 할 수 있도록 설정하려고합니다. 이 데이터베이스를 설정하는 방법에 대한 제안 사항이 있습니까? 이것이 일대일 관계를 올바르게 사용하는지 또는 더 좋은 방법이 있는지 확실하지 않습니다. 시간을내어이 문제를 검토해 주셔서 감사합니다.

내가 읽은 다른 스레드 중 일부는 다음과 같습니다. 그들은 나에게 좋은 통찰력을 주었지만 실제로 적용되지는 않는다고 생각합니다.

/programming/9335548/how-to-structure-database-for-inventory-of-unlike-items

/programming/1249632/database-structure-for-items-with-varying-attributes

/programming/5559587/product-inventory-with-multiple-attributes

/programming/6613802/question-about-setting-up-inventory-database

/programming/514111/how-to-best-represent-items-with-variable-of-attributes-in-a-database

답변:


6

수퍼 타입 ​​/ 서브 타입

수퍼 타입 ​​/ 서브 타입 패턴을 살펴 보는 것은 어떻습니까? 공통 열은 상위 테이블로 이동합니다. 각 고유 한 유형에는 고유 한 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 트리거의 끔찍한 고통스러운 세부 사항에 관심이 있다면 알려 주시면 게시하겠습니다. 샘플입니다.)

그리고 하나 더 아이디어

모든 데이터를 하나의 테이블에 넣습니다. 열에 일반 이름을 지정한 다음 여러 용도로 재사용 / 남용하십시오. 이들에 대한보기를 작성하여 합리적인 이름을 지정하십시오. 적합한 데이터 유형의 사용하지 않는 열을 사용할 수없는 경우 열을 추가하고보기를 업데이트하십시오. 하위 유형 / 슈퍼 유형에 대한 나의 길이에도 불구하고 이것이 가장 좋은 방법 일 수 있습니다.


각 하위 유형 테이블이 상위 및 공통 필드의 PK를 갖는이 디자인을 생각했습니다. 부모와 각 하위 유형 테이블에 유형 필드를 넣은 다음 CHECK 제약 조건을 적용 할 수 있다고 생각했습니다. 새로운 유형의 장치를 추적해야 할 때마다 새로운 테이블이 필요하고 일대일 관계가 많기 때문에이 디자인을 피하기로 결정했습니다. 지저분하고 융통성이 없어 보였다. 그래도 귀하의 의견에 감사드립니다.
TheSecretSquad

비즈니스에서 사용중인 EAV 데이터베이스를 구축했습니다. 감사합니다. 데이터 세트가 작으므로 (수십 개의 항목 유형이 있지만) 성능이 나쁘지 않습니다. 그러나 데이터베이스에 수천 개가 넘는 항목이있는 경우입니다. 이 경험을 통해 나는 미래에 EAV 데이터베이스를 피하고 싶기 때문에 앞으로는 EAV 데이터베이스를 피하고 싶었습니다.
ErikE

또한 실제 하위 유형 테이블의 사용 / 생성 / 수정을 자동화하는 데 소비 된 시간은 궁극적으로 가장 좋습니다.
ErikE

EAV 패턴을 조사한 후 속성 값이 데이터 유형 (이 경우 모든 문자열)을 공유해야한다는 것을 깨달았습니다. 또한 EAV 설정을 쿼리하는 것이 번거로운 일입니다. 수퍼 타입 ​​/ 서브 타입이 더 좋아 보입니다. 내 질문은 지금 특정 테이블은 특정 장치 유형 만 허용한다는 것입니다. 모든 테이블에 장치 클래스 ID (전화, 컴퓨터, 라우터)를 넣고 해당 필드에 검사 제한을 두거나 하위 필드 테이블에서 해당 필드를 제외하고 각각에 대해 트리거를 사용하여이를 검증합니까? 참고로 ERD3 를 참조하십시오.
TheSecretSquad

1
EAV 데이터를 쿼리 할 때 쿼리하려는 데이터에 대한 관계형 테이블의 데이터 마트를 구축 한 다음 일부 스크립트를 사용하여 데이터를 채우는 것은 드문 일이 아닙니다. 쿼리는 더 빠르게 실행되지만 데이터 마트에 넣은 데이터에 대해서만 실행되며 설정에는 약간의 계획이 필요합니다.
FrustratedWithFormsDesigner

6

귀하의 경우 가장 좋은 방법은 EAV (Entity-Attribute-Value) 모델의 변형입니다. EAV는 어떤 식 으로든 도움이되지 않고 많은 시간을 잘못 사용했기 때문에 EAV를 멀리하는 사람들이 많이 있습니다. 그러나 EAV는 특정 요구 사항에 잘 맞는 솔루션입니다.

상황에 포함시키려는 변형은 엔티티 (예 : 인벤토리 항목)에서 한 레벨 떨어진 속성을 추상화하는 것입니다. 기본적 으로 속성 목록이있는 장치 유형 을 정의하려고 합니다. 그런 다음 해당 유형의 장치가 갖는 각 속성에 대한 값이있는 장치 인스턴스 를 정의 합니다 .

ERD 스케치는 다음과 같습니다.

ERD

DEVICE_ATTRIBUTE각 일반 속성 유형에 대한 값을 포함합니다. DEVICE_TYPE주어진 유형의 장치에 적용되는 일반 속성 목록을 정의합니다 TYPICAL_DEVICE_ATTRIBUTEs.

이를 통해 장치에 대해 어떤 속성을 작성해야하는지 제어 할 수 있으며 다른 유형의 장치는 다른 속성 목록을 가질 수 있습니다. 또한 다른 장치와 비교하여 장치의 특성을 쉽게 비교할 수 있습니다.


이것은 ssmusoke가 권장하는 것과 유사합니다. 나는 그의 추천을 사용하여 ERD를 변경했으며 그것이 당신의 것과 일치하는 것처럼 보입니다. http://www.dividegraphics.com/ERD2.jpg 에서 새로운 RD를 확인 하고 의견 을 보내주십시오 .
TheSecretSquad

@reallythecrash-당신은 맞습니다, 나는 ssmusoke와 동일한 기본 접근 방식을 제안하고 있습니다. 모델의 구조와 EAV를 사용하기위한 근거를 더 쉽게 이해할 수 있기를 희망하면서 내 대답에 다른 방법을 택했습니다. 많은 사람들이 (부당하게) 반 패턴으로 비난합니다.
Joel Brown

몇 가지 연구를 한 결과 사람들이 왜 EAV를 반 패턴으로 간주하는지 알 수 있습니다. EAV를 사용하여 데이터를 저장하는 것이 간단하지만 데이터 유형을 쿼리하고 유지 관리하는 데 특히 복잡하다고 생각합니다. 나는 그것이 좁은 목적을 가진 패턴이라고 생각하며 그것을 제대로 구현할 수있는 숙련 된 개발자가 사용해야합니다. 아마도 슈퍼 타입 / 서브 타입 패러다임을 선택할 것입니다.
TheSecretSquad 5

@JoelBrown-다이어그램을 스케치하기 위해 어떤 소프트웨어를 사용 했습니까?
Vidar

@Vidar-James Martin의 시각적 규칙을 사용하기 위해 만든 ERD 스마트 셰이프와 함께 Visio를 사용하고 스케치 한 사용자 지정 선 패턴으로 그렸습니다. 빠른 / 초안 데이터 모델에 유용한 도구입니다. 다이어그램이 너무 형식적이면 일부 사람들은 완성 된 것으로 생각할 수 있으므로 스케치가 있으면 사람들이 데이터 모델이 얼마나 강력하고 완성되었는지에 대한 결론으로 ​​넘어 가지 못하게하는 데 도움이됩니다.
Joel Brown

1
  1. 전반적인 접근 방식은 다음과 같습니다.

a) 다른 장치의 속성을 장치 유형으로 처리하기위한 Entity-Attribute-Value 모델 접근 방식. 각 장치 유형에는 값을 추적하는 속성 목록이 있습니다.

b) 각 장치 유형에 대해 단일 장치에 해당하는 일련 번호로 인벤토리 세부 정보를 추적합니다.

  1. 따라서 다음 테이블로 끝납니다.

a) 속성-모든 장치 (이 테이블에있는 모든 것) 열에 대한 속성을 정의합니다 : id, 이름, 설명

b) 아이템 속성-특정 장치에 허용되는 속성을 정의합니다-itemid, attributeid

c) 항목 정의-Black Berry Torch 4500, Iphone 4S, Iphone 3S 등의 항목을 정의합니다-id, 이름, 설명, categoryid (휴대 전화, 스위치 등과 같은 범주를 추가하려는 경우)

d) 장치-개별 장치-id, itemid, inventorydate, deactivatedate, serialnumber ... (기본적으로 장치의 다른 모든 속성)

장치 전환에 대한 다른 정보를 추적하려는 경우 필요에 따라 장치에 연결된 테이블을 더 추가 할 수 있습니다.


입력 해 주셔서 감사합니다. 이것은 내가 찾고있는 것과 인라인이며, 나는 그것을하는 방법을 알 수 없었습니다. 귀하의 사양을 반영하기 위해 ERD를 변경했습니다. 각 장치 유형에 허용되는 모든 속성을 입력하는 데 더 많은 작업이 필요한 것 같지만 최대한의 유연성을 제공하는 것 같습니다. 작은 프로토 타입을 만들어 생각대로 작동하는지 확인해 보겠습니다. 다시 감사합니다. 보고 싶을 때 변경 사항이 포함 된 ERD를 업로드하고 올바른 궤도에 있는지 알려주세요. http://www.dividegraphics.com/ERD2.jpg
TheSecretSquad

예, 당신은 올바른 길을 가고 있습니다.
Stephen Senkomago Musoke

EAV는 많은 유연성을 제공하지만 작동을 유지하기 위해 더 많은 메타 데이터가 남아 있습니다.
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner는 시스템이 다양한 아이템, 전화, 스위치, PC, 랩톱 등을 저장할 때 피할 수없는 것처럼 보입니다 ... 내가 말하고 싶은 테이블보다 더 많은 메타 데이터
Stephen Senkomago Musoke

1
@ ssmusoke : 동의하지만 사람들이 메타 데이터의 중요성을 깨닫지 못하는 것을 보았으므로 EAV 구현이 악몽이되기 때문에 그 점을 강조하고 싶었습니다.
FrustratedWithFormsDesigner
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.