Supertype / sous-type
Que diriez-vous d'examiner le modèle de supertype / sous-type? Les colonnes communes vont dans une table parent. Chaque type distinct a sa propre table avec l'ID du parent comme son propre PK et il contient des colonnes uniques qui ne sont pas communes à tous les sous-types. Vous pouvez inclure une colonne de type dans les tables parent et enfant pour vous assurer que chaque appareil ne peut pas être plus d'un sous-type. Créez un FK entre les enfants et le parent (ItemID, ItemTypeID). Vous pouvez utiliser des FK pour les tables de supertype ou de sous-type pour maintenir l'intégrité souhaitée ailleurs. Par exemple, si le ItemID de n'importe quel type est autorisé, créez le FK dans la table parent. Si seul SubItemType1 peut être référencé, créez le FK dans cette table. Je laisserais le TypeID hors des tables de référence.
Appellation
Quand il s'agit de nommer, vous avez deux choix comme je le vois (puisque le troisième choix de juste "ID" est dans mon esprit un fort anti-modèle). Appelez la clé de sous-type ItemID comme dans la table parent ou appelez-le le nom de sous-type tel que DoohickeyID. Après réflexion et expérience, je préconise de l'appeler DoohickeyID. La raison en est que même s'il peut y avoir confusion sur la table de sous-types contenant des éléments (plutôt que Doohickeys) déguisés, c'est un petit point négatif par rapport à la création d'un FK dans la table Doohickey et les noms de colonne ne le font pas. rencontre!
Pour EAV ou pas pour EAV - Mon expérience avec une base de données EAV
Si l'EAV est vraiment ce que vous devez faire, c'est ce que vous devez faire. Mais si ce n'était pas ce que tu devais faire?
J'ai construit une base de données EAV qui est utilisée dans une entreprise. Dieu merci, l'ensemble de données est petit (bien qu'il existe des dizaines de types d'éléments), les performances ne sont donc pas mauvaises. Mais ce serait mauvais si la base de données contenait plus de quelques milliers d'articles! De plus, les tables sont si DIFFICILES à interroger. Cette expérience m'a conduit à vraiment vouloir éviter les bases de données EAV à l'avenir si possible.
Maintenant, dans ma base de données, j'ai créé une procédure stockée qui crée automatiquement des vues PIVOTed pour chaque sous-type existant. Je peux simplement interroger AutoDoohickey. Mes métadonnées sur les sous-types ont une colonne "ShortName" contenant un nom sûr pour les objets pouvant être utilisé dans les noms de vue. J'ai même rendu les vues modifiables! Malheureusement, vous ne pouvez pas les mettre à jour lors d'une jointure, mais vous POUVEZ y insérer une ligne déjà existante, qui sera convertie en UPDATE. Malheureusement, vous ne pouvez pas mettre à jour seulement quelques colonnes, car il n'y a aucun moyen d'indiquer au VIEW quelles colonnes vous souhaitez mettre à jour avec le processus de conversion INSERT-to-UPDATE: une valeur NULL ressemble à "mettre à jour cette colonne en NULL" même si vous vouliez indiquer "Ne pas mettre à jour cette colonne du tout".
Malgré toute cette décoration pour rendre la base de données EAV plus facile à utiliser, je n'utilise toujours pas ces vues dans la plupart des requêtes normales car elle est LENTE. Les conditions de requête ne sont pas des prédicats repoussés jusqu'au Value
tableau, il doit donc créer un jeu de résultats intermédiaire de tous les éléments du type de cette vue avant le filtrage. Aie. J'ai donc beaucoup, beaucoup de requêtes avec beaucoup, beaucoup de jointures, chacune sortant pour obtenir une valeur différente et ainsi de suite. Ils fonctionnent relativement bien, mais aïe! Voici un exemple. Le SP qui crée cela (et son déclencheur de mise à jour) est une bête géante, et j'en suis fier, mais ce n'est pas quelque chose que vous voulez essayer de maintenir.
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;
Voici un autre type de vue générée automatiquement créée par une autre procédure stockée à partir de métadonnées spéciales pour aider à trouver des relations entre des éléments qui peuvent avoir plusieurs chemins entre eux (en particulier: Module-> Server, Module-> Cluster-> Server, Module-> DBMS- > Serveur, Module-> SGBD-> Cluster-> Serveur):
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'approche hybride
Si vous DEVEZ avoir certains des aspects dynamiques d'une base de données EAV, vous pouvez envisager de créer les métadonnées comme si vous aviez une telle base de données, mais en utilisant plutôt le modèle de conception de supertype / sous-type. Oui, vous devrez créer de nouvelles tables, ajouter et supprimer et modifier des colonnes. Mais avec le prétraitement approprié (comme je l'ai fait avec les vues automatiques de ma base de données EAV), vous pourriez avoir de vrais objets de type table avec lesquels travailler. Seulement, ils ne seraient pas aussi noueux que le mien et l'optimiseur de requêtes pourrait pousser les prédicats vers les tables de base (lire: bien fonctionner avec eux). Il y aurait juste une jointure entre la table de supertype et la table de sous-type. Votre application peut être configurée pour lire les métadonnées pour découvrir ce qu'elle est censée faire (ou elle peut utiliser les vues générées automatiquement dans certains cas).
Ou, si vous aviez un ensemble de sous-types à plusieurs niveaux, juste quelques jointures. Par multi-niveaux, je veux dire que lorsque certains sous-types partagent des colonnes communes, mais pas tous, vous pouvez avoir une table de sous-types pour celles qui est elle-même un super-type de quelques autres tables. Par exemple, si vous stockez des informations sur les serveurs, les routeurs et les imprimantes, un sous-type intermédiaire de «périphérique IP» pourrait avoir un sens.
Je ferai la mise en garde que je n'ai pas encore créé une telle base de données hybride supertype / sous-type décorable par EAV comme je suggère ici d'essayer dans le monde réel. Mais les problèmes que j'ai rencontrés avec l'EAV ne sont pas petits, et faire quelque chose est probablement un must absolu si votre base de données va être volumineuse et que vous voulez de bonnes performances sans un matériel gigantesque et fou.
À mon avis, le temps passé à automatiser l'utilisation / la création / la modification de tables de sous-types réels serait finalement le meilleur. Se concentrer sur la flexibilité tirée par les données rend le son de l'EAV si attrayant (et croyez-moi, j'aime la façon dont quand quelqu'un me demande un nouvel attribut sur un type d'élément, je peux l'ajouter en environ 18 secondes et il peut immédiatement commencer à entrer des données sur le site Web ). Mais la flexibilité peut être accomplie de plusieurs façons! Le prétraitement est une autre façon de le faire. C'est une méthode si puissante que si peu de gens l'utilisent, ce qui donne les avantages d'être totalement basé sur les données mais la performance d'être codé en dur.
(Remarque: Oui, ces vues sont vraiment formatées comme ça et celles de PIVOT ont vraiment des déclencheurs de mise à jour. :) Si quelqu'un est vraiment intéressé par les détails terribles et douloureux du déclencheur de mise à jour long et compliqué, faites le moi savoir et je posterai un échantillon pour vous.)
Et une idée de plus
Mettez toutes vos données dans un seul tableau. Donnez aux colonnes des noms génériques, puis réutilisez-les / abusez-les à des fins multiples. Créez des vues dessus pour leur donner des noms sensés. Ajoutez des colonnes lorsqu'une colonne inutilisée de type de données approprié n'est pas disponible et mettez à jour vos vues. Malgré ma longueur sur le sous-type / supertype, cela peut être le meilleur moyen.
Dans votre cas, la meilleure approche est une variation du modèle Entity-Attribute-Value (EAV). Il y a beaucoup de gens qui évitent l'EAV parce que cela ne sert à rien à certains égards et est mal utilisé la plupart du temps. Cependant, EAV est une solution qui fonctionne bien pour vos besoins spécifiques.
La variation que vous souhaitez inclure pour votre situation est d'abstraire les attributs à un niveau de vos entités (c'est-à-dire vos articles en stock). Essentiellement, vous souhaitez définir des types d'appareils qui ont une liste d'attributs. Ensuite, vous définissez des instances de périphérique qui ont des valeurs pour chacun des attributs que les périphériques de ce type sont censés avoir.
Voici un croquis ERD:
DEVICE_ATTRIBUTE
contient les valeurs de chaque type d'attribut générique.DEVICE_TYPE
définit la liste des attributs génériques qui s'appliquent à un type d'appareil donné (ce sont lesTYPICAL_DEVICE_ATTRIBUTEs
.Cela vous permet de contrôler quels attributs doivent être remplis pour un périphérique tout en laissant des périphériques de type différent avoir des listes d'attributs différentes. Il vous permet également de comparer facilement entre les appareils en alignant leurs attributs les uns contre les autres.
la source
a) Une approche de modèle Entité-Attribut-Valeur pour aborder les attributs des différents dispositifs à un type de dispositif. Chaque type d'appareil aura une liste d'attributs dont vous suivez les valeurs
b) Pour chaque type d'appareil, vous suivez les détails de l'inventaire par numéro de série qui correspond à un seul appareil.
a) Attributs - définissez les attributs de tous les appareils (tout ce qui se passe dans ce tableau) colonnes: id, nom, description
b) Attributs d'article - définit les attributs autorisés pour un périphérique spécifique - itemid, attributeid
c) Définition d'élément - définit un élément, par exemple Black Berry Torch 4500, Iphone 4S, Iphone 3S, etc. - id, nom, description, categoryid (si vous souhaitez ajouter des catégories comme les téléphones mobiles, les commutateurs, etc.)
d) Périphériques - les périphériques individuels - id, itemid, inventaire, désactivé, numéro de série ... (essentiellement tous les autres attributs pour un périphérique)
Si vous souhaitez suivre toute autre information sur les transcations de l'appareil, vous pouvez ajouter d'autres tableaux liés à l'appareil selon vos besoins.
la source