Struttura del database di inventario quando gli articoli di inventario hanno attributi variabili


10

Sto creando un database di inventario per archiviare informazioni sull'hardware aziendale. I dispositivi che il database tiene traccia della portata da stazioni di lavoro, laptop, switch, router, telefoni cellulari, ecc. Sto usando i numeri di serie del dispositivo come chiave primaria. Il problema che sto riscontrando è che gli altri attributi di questi dispositivi variano e non voglio avere campi nella tabella di inventario non correlati ad altri dispositivi. Di seguito è riportato un collegamento a un ERD di parte del database (alcune relazioni FK non sono mostrate). Sto provando a configurarlo, ad esempio, quindi un dispositivo con un tipo di dispositivo workstation non può essere inserito nella tabella dei telefoni. Ciò sembra richiedere l'uso di molti trigger per convalidare il tipo o la classe di dispositivo e nuove tabelle ogni volta che verrà tracciato un dispositivo diverso con attributi diversi;

Erd1

Ho esaminato la configurazione di tabelle di attributi che possono essere associate a numeri di serie, ma che consentirebbe di assegnare a un dispositivo attributi che non si applicano a un tipo di dispositivo, ad esempio qualcuno potrebbe assegnare un attributo di numero di telefono a una workstation se lo desidera . Ho trovato una spiegazione su questo sito che ha fornito la seguente struttura:

Widget Esempio ERD

Questa struttura funzionerebbe alla grande se gli attributi fossero tutti applicabili agli articoli che sto memorizzando. Ad esempio se il database memorizzava solo telefoni cellulari, gli attributi potrebbero essere cose come touchscreen, trackpad, tastiera, 4G, 3G ... qualunque cosa. In tal caso, si applicano tutti ai telefoni. Il mio database avrebbe attributi come hostname, circuitType, phoneNumber, che si applicano solo a tipi specifici di dispositivi.

Voglio configurarlo in modo che solo gli attributi che si applicano a un determinato tipo di dispositivo possano essere assegnati a un dispositivo di quel tipo. Qualche suggerimento su come impostare questo database? Non sono sicuro se questo è un uso corretto delle relazioni uno a uno o se esiste un modo migliore per farlo. Grazie in anticipo per aver dedicato del tempo a esaminare questo aspetto.

Ecco alcuni degli altri thread che ho letto. Mi hanno dato alcune buone intuizioni, ma non credo che si applichino davvero:

/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

Risposte:


6

Supertype / Sottotipo

Che ne dici di esaminare il modello supertipo / sottotipo? Le colonne comuni vanno in una tabella principale. Ogni tipo distinto ha una propria tabella con l'ID del genitore come proprio PK e contiene colonne univoche non comuni a tutti i sottotipi. È possibile includere una colonna di tipo nelle tabelle padre e figlio per assicurarsi che ciascun dispositivo non possa essere più di un sottotipo. Crea un FK tra i figli e il genitore su (ItemID, ItemTypeID). È possibile utilizzare gli FK per le tabelle dei sottotipi o dei sottotipi per mantenere l'integrità desiderata altrove. Ad esempio, se è consentito ItemID di qualsiasi tipo, creare l'FK nella tabella padre. Se è possibile fare riferimento solo a SubItemType1, creare l'FK in quella tabella. Vorrei lasciare il TypeID fuori dalle tabelle di riferimento.

Naming

Quando si tratta di nominare, hai due scelte come la vedo io (poiché la terza scelta di solo "ID" è nella mia mente un forte anti-pattern). Chiamare la chiave del sottotipo ItemID come se fosse nella tabella padre o chiamarla con il nome del sottotipo come DoohickeyID. Dopo un po 'di pensiero e un po' di esperienza con questo, sostengo chiamandolo DoohickeyID. La ragione di ciò è che anche se ci potrebbe essere confusione sulla tabella dei sottotipi in realtà sotto mentite spoglie contenenti Oggetti (piuttosto che Doohickeys), questo è un piccolo aspetto negativo rispetto a quando si crea un FK nella tabella Doohickey e i nomi delle colonne non lo fanno incontro!

A EAV o no a EAV - La mia esperienza con un database EAV

Se EAV è ciò che devi veramente fare, allora è ciò che devi fare. E se non fosse quello che dovevi fare?

Ho creato un database EAV in uso in un'azienda. Grazie a Dio, l'insieme di dati è piccolo (anche se ci sono dozzine di tipi di oggetti) quindi le prestazioni non sono male. Ma sarebbe male se nel database fossero presenti più di qualche migliaio di voci! Inoltre, le tabelle sono così DURE da interrogare. Questa esperienza mi ha portato a desiderare davvero di evitare i database EAV in futuro, se possibile.

Ora, nel mio database ho creato una procedura memorizzata che crea automaticamente viste PIVOT per ogni sottotipo esistente. Posso solo interrogare da AutoDoohickey. I miei metadati sui sottotipi hanno una colonna "ShortName" contenente un nome sicuro per gli oggetti adatto per l'uso nei nomi delle viste. Ho persino reso le visualizzazioni aggiornabili! Sfortunatamente, non puoi aggiornarli su un join, ma PUOI inserire loro una riga già esistente, che verrà convertita in un AGGIORNAMENTO. Sfortunatamente, non è possibile aggiornare solo alcune colonne, poiché non è possibile indicare a VIEW quali colonne si desidera aggiornare con il processo di conversione INSERT-to-UPDATE: un valore NULL sembra "aggiorna questa colonna a NULL" anche se volevi indicare "Non aggiornare affatto questa colonna".

Nonostante tutta questa decorazione per rendere più facile da usare il database EAV, non uso ancora queste viste nella maggior parte delle query normali perché è LENTO. Le condizioni della query non vengono reinserite nella Valuetabella fino al predicato , quindi deve creare un set di risultati intermedio di tutti gli elementi del tipo di quella vista prima di filtrare. Ahia. Quindi ho molte, molte domande con molti, molti join, ognuno uscendo per ottenere un valore diverso e così via. Si esibiscono relativamente bene, ma ahi! Ecco un esempio L'SP che crea questo (e il suo trigger di aggiornamento) è una bestia gigante, e ne sono orgoglioso, ma non è qualcosa che vorresti mai provare a mantenere.

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;

Ecco un altro tipo di vista generata automaticamente creata da un'altra procedura memorizzata da metadati speciali per aiutare a trovare relazioni tra elementi che possono avere più percorsi tra di loro (In particolare: Modulo-> Server, Modulo-> Cluster-> Server, Modulo-> DBMS- > Server, Modulo-> DBMS-> Cluster-> Server):

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

L'approccio ibrido

Se DEVI avere alcuni degli aspetti dinamici di un database EAV, potresti prendere in considerazione la creazione dei metadati come se avessi un tale database, ma invece in realtà utilizzi il modello di progettazione supertipo / sottotipo. Sì, dovresti creare nuove tabelle, aggiungere, rimuovere e modificare colonne. Ma con la corretta pre-elaborazione (come ho fatto con le viste automatiche del mio database EAV) potresti avere oggetti simili a tabelle con cui lavorare. Solo, non sarebbero così nodosi come il mio e Query Optimizer potrebbe prevedere il push down alle tabelle di base (leggi: esegui bene con loro). Ci sarebbe solo un join tra la tabella dei supertipi e la tabella dei sottotipi. La tua applicazione potrebbe essere impostata per leggere i metadati per scoprire cosa dovrebbe fare (o in alcuni casi può utilizzare le viste generate automaticamente).

Oppure, se disponevi di un set multi-livello di sottotipi, solo alcuni join. Per multi-livello intendo quando alcuni sottotipi condividono colonne comuni, ma non tutti, potresti avere una tabella dei sottotipi per quelli che è esso stesso un supertipo di poche altre tabelle. Ad esempio, se si memorizzano informazioni su server, router e stampanti, un sottotipo intermedio di "Dispositivo IP" potrebbe avere senso.

Darò l'avvertenza che non ho ancora creato un database ibrido con super-tipo / sottotipo EAV-metatable-decorato come sto suggerendo qui di provare ancora nel mondo reale. Ma i problemi che ho riscontrato con EAV non sono piccoli, e fare qualcosa è probabilmente un must assoluto se il tuo database sarà grande e desideri buone prestazioni senza un hardware gigantesco e costoso.

Secondo me, il tempo impiegato per automatizzare l'uso / la creazione / la modifica di tabelle di sottotipi reali sarebbe in definitiva la cosa migliore. Concentrarsi sulla flessibilità guidata dai dati rende l'EAV un suono così attraente (e credetemi, mi piace come quando qualcuno mi chiede un nuovo attributo su un tipo di elemento posso aggiungerlo in circa 18 secondi e iniziare immediatamente a inserire i dati sul sito web ). Ma la flessibilità può essere raggiunta in più di un modo! La pre-elaborazione è un altro modo per farlo. È un metodo così potente che così poche persone usano, dando i vantaggi di essere totalmente guidato dai dati ma le prestazioni di essere codificato.

(Nota: Sì, quelle viste sono davvero formattate in quel modo e quelle PIVOT hanno davvero dei trigger di aggiornamento. :) Se qualcuno è davvero così interessato ai terribili dettagli dolorosi del lungo e complicato trigger UPDATE, fammi sapere e posterò un campione per te.)

E un'altra idea

Metti tutti i tuoi dati in una tabella. Dai alle colonne nomi generici e poi riutilizzali / abusali per molteplici scopi. Crea viste su questi per dare loro nomi sensibili. Aggiungi colonne quando non è disponibile una colonna inutilizzata del tipo di dati adatto e aggiorna le visualizzazioni. Nonostante la mia lunghezza sul sottotipo / supertipo, questo potrebbe essere il modo migliore.


Ho pensato a questo design in cui ogni tabella dei sottotipi aveva il PK dal genitore e i campi non comuni. Ho pensato di poter inserire il campo del tipo nel genitore e in ogni tabella dei sottotipi e quindi inserire un vincolo CHECK su di essi. Ho deciso di evitare questo progetto perché richiederebbe un nuovo tavolo ogni volta che deve essere tracciato un nuovo tipo di dispositivo e molte relazioni uno a uno. Sembrava disordinato e poco flessibile. Apprezzo il tuo contributo però.
TheSecretSquad

Ho creato un database EAV in uso in un'azienda. Grazie a Dio, l'insieme di dati è piccolo (anche se ci sono dozzine di tipi di oggetti) quindi le prestazioni non sono male. Ma sarebbe se il database avesse più di qualche migliaio di elementi. Questa esperienza mi ha portato a desiderare davvero di evitare i database EAV in futuro, se possibile, perché sono così difficili da interrogare.
ErikE

Inoltre, a mio avviso, il tempo impiegato per automatizzare l'uso / la creazione / la modifica di tabelle di sottotipi reali sarebbe, in definitiva, il migliore.
ErikE

Dopo aver esaminato il modello EAV, mi sono reso conto che i valori per gli attributi sono costretti a condividere un tipo di dati (in questo caso tutta la stringa). Inoltre, interrogare la configurazione EAV sarà un lavoro ingrato. Il sottotipo / sottotipo ha un aspetto migliore. La mia domanda ora è che alcune tabelle consentono solo tipi di dispositivi specifici. Convalido questo inserendo un ID classe dispositivo (telefono, computer, router) in ogni tabella e inserendo un vincolo di controllo su quel campo, oppure escludo quel campo dalle tabelle dei sottotipi e utilizzo un trigger su ciascuno di essi? Vedere ERD3 per riferimento.
TheSecretSquad

1
Per eseguire query sui dati EAV, non è insolito creare un datamart di tabelle relazionali per i dati che si desidera interrogare e quindi popolarli utilizzando alcuni script. Le query verranno eseguite più rapidamente, ma solo rispetto ai dati inseriti nel datamart e l'installazione richiede un bel po 'di pianificazione.
FrustratedWithFormsDesigner

6

Nel tuo caso l'approccio migliore è una variazione del modello Entity-Attribute-Value (EAV). Ci sono molte persone che evitano EAV perché è in qualche modo inutile e usato male per la maggior parte del tempo. Tuttavia, EAV è una soluzione che funziona bene per le tue esigenze specifiche.

La variazione che desideri includere per la tua situazione è quella di sottrarre gli attributi ad un livello di distanza dalle tue entità (cioè i tuoi oggetti di inventario). In sostanza, si desidera definire i tipi di dispositivo con un elenco di attributi. Quindi si definiscono le istanze del dispositivo che hanno valori per ciascuno degli attributi che dovrebbero avere i dispositivi di quel tipo.

Ecco uno schizzo ERD:

ERD

DEVICE_ATTRIBUTEcontiene i valori per ciascun tipo di attributo generico. DEVICE_TYPEdefinisce l'elenco di attributi generici che si applicano a un determinato tipo di dispositivo (questi sono i TYPICAL_DEVICE_ATTRIBUTEs.

Ciò consente di controllare quali attributi devono essere compilati per un dispositivo, consentendo a dispositivi di tipo diverso di avere elenchi di attributi diversi. Inoltre, ti consente di confrontare facilmente tutti i dispositivi allineando gli attributi uno contro l'altro.


Sembra simile a ciò che ssmusoke ha raccomandato. Ho cambiato il mio ERD usando la sua raccomandazione e sembra che corrisponda al tuo. Sentiti libero di dare un'occhiata al nuovo RD su http://www.dividegraphics.com/ERD2.jpg e di fornire qualsiasi feedback.
TheSecretSquad

@reallythecrash - Hai ragione, sto suggerendo lo stesso approccio di base di ssmusoke, ho appena preso una decisione diversa sulla mia risposta nella speranza di rendere più semplice sia la struttura del modello sia la logica dell'utilizzo di EAV che molte persone (ingiustamente) denunciano come un anti-schema.
Joel Brown,

Dopo alcune ricerche, vedo perché le persone potrebbero considerare l'EAV un anti-pattern. Penso che sia semplice archiviare i dati utilizzando EAV, ma particolarmente complesso per interrogare e mantenere i tipi di dati. Penso che sia un modello con uno scopo ristretto e dovrebbe essere utilizzato da sviluppatori esperti che possono implementarlo correttamente, cioè non io. Probabilmente opterò per il paradigma supertipo / sottotipo.
TheSecretSquad

@JoelBrown - quale software hai usato per disegnare quel diagramma?
Vidar,

@Vidar - Ho usato Visio con le forme intelligenti ERD che ho creato per usare le convenzioni visive di James Martin e disegnato con un motivo a linee personalizzato che è abbozzato. Trovo che questo sia un buon strumento da utilizzare per modelli di dati rapidi / bozza. Quando il diagramma è troppo formale può portare alcune persone a pensare che sia finito, quindi qualcosa di abbozzato aiuta a impedire alle persone di saltare alle conclusioni su quanto sia solido / finito un modello di dati.
Joel Brown,

1
  1. L'approccio generale è il seguente:

a) Un approccio modello Entità-Attributo-Valore per affrontare gli attributi dei diversi dispositivi in ​​un tipo di dispositivo. Ogni tipo di dispositivo avrà un elenco di attributi di cui segui i valori

b) Per ciascun tipo di dispositivo, è possibile tenere traccia dei dettagli dell'inventario in base al numero seriale che corrisponde a un singolo dispositivo.

  1. Quindi finiresti con le seguenti tabelle:

a) Attributi: definisci gli attributi per tutte le colonne dei dispositivi (tutto va bene in questa tabella): id, nome, descrizione

b) Attributi oggetto: definisce gli attributi consentiti per un dispositivo specifico: itemid, attributo

c) Definizione elemento: definisce un elemento che dice Black Berry Torch 4500, Iphone 4S, Iphone 3S ecc. - ID, nome, descrizione, categoryid (se si desidera aggiungere categorie come telefoni cellulari, interruttori ecc.)

d) Dispositivi: i singoli dispositivi: ID, itemid, inventario, disattivato, numero seriale ... (praticamente tutti gli altri attributi per un dispositivo)

Se desideri tenere traccia di qualsiasi altra informazione sulle transazioni del dispositivo, puoi aggiungere altre tabelle collegate al dispositivo di cui hai bisogno.


Grazie per il tuo contributo. Questo è in linea con quello che sto cercando, non riuscivo proprio a capire come farlo. Ho modificato il mio ERD per riflettere le tue specifiche. Sembra che richieda più lavoro per inserire tutti gli attributi consentiti per ciascun tipo di dispositivo, ma sembra anche che offra la massima flessibilità. Farò un piccolo prototipo per vedere se funziona come penso. Grazie ancora. Ho caricato un ERD con le modifiche se vuoi dare un'occhiata e fammi sapere se sono sulla strada giusta. http://www.dividegraphics.com/ERD2.jpg
TheSecretSquad

Sì, sei sulla strada giusta.
Stephen Senkomago Musoke,

EAV offrirà molta flessibilità, ma hai anche molti più metadati in giro per farlo funzionare.
FrustratedWithFormsDesigner

@FrustratedWithFormsDesigner sembra inevitabile quando il sistema memorizza una vasta gamma di articoli, telefoni, switch, pc, laptop, ecc ... Meglio più metadati di più tabelle direi
Stephen Senkomago Musoke

1
@ssmusoke: Sono d'accordo, ma volevo sottolineare questo punto perché ho visto le persone non capire l'importanza dei metadati, e quindi la loro implementazione EAV diventa un incubo.
FrustratedWithFormsDesigner
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.