Je cherche à utiliser deux colonnes de la table A comme clés étrangères pour l'une des deux tables: Table B ou Table C.En utilisant les colonnes table_a.item_id et table_a.item_type_id, je veux forcer toutes les nouvelles lignes à avoir un item_id et un item_type_id correspondants dans le tableau B ou le tableau C.
Exemple:
ALTER TABLE [inventory] ADD CONSTRAINT [FK_inventory_sources] FOREIGN KEY ([item_id],[item_type_id]) REFERENCES {[products] ([id],[item_type_id]) OR [recipes] ([id],[item_type_id])}
Je veux pouvoir avoir un tableau d'inventaire où Les employés peuvent saisir des inventaires, que l’article soit une recette ou un produit. Je ne veux pas avoir une table product_inventory et recette_inventory car il y a de nombreuses opérations que je dois faire sur tous les articles de l'inventaire, quels que soient les types d'articles.
Une solution serait de créer une table de référence comme celle-ci :
CREATE TABLE [dbo].[inventory] ( [id] [bigint] IDENTITY(1,1) NOT NULL, [item_id] [smallint] NOT NULL, [item_type_id] [tinyint] NOT NULL, [count] [float] NOT NULL, CONSTRAINT [PK_inventory_id] PRIMARY KEY CLUSTERED ([id] ASC) ) ON [PRIMARY]
Cela semble juste très encombrant, et je devrais maintenant ajouter / supprimer des produits / recettes de cette nouvelle table chaque fois qu'ils sont ajoutés / supprimés de leurs tables respectives. (Existe-t-il un moyen automatique d'y parvenir?)
Table CD: Items +---------+--------------+------------+-----------+ | item_id | item_type_id | product_id | recipe_id | +---------+--------------+------------+-----------+ | 2 | 1 | NULL | 2 | | 3 | 1 | NULL | 3 | | 1 | 2 | 1 | NULL | +---------+--------------+------------+-----------+
Ce que j'aimerais vraiment faire, c'est quelque chose comme ça ...
Table A: Inventory +---------+--------------+-------+ | item_id | item_type_id | count | +---------+--------------+-------+ | 2 | 1 | 32 | | 3 | 1 | 24 | | 1 | 2 | 10 | +---------+--------------+-------+ Table B: Recipes +----+--------------+-------------------+-------------+----------------------+ | id | item_type_id | name | consistency | gram_to_fluid_ounces | +----+--------------+-------------------+-------------+----------------------+ | 1 | 1 | Delicious Juice | thin | .0048472 | | 2 | 1 | Ok Tasting Juice | thin | .0057263 | | 3 | 1 | Protein Smoothie | heavy | .0049847 | +----+--------------+-------------------+-------------+----------------------+ Table C: Products +----+--------------+----------+--------+----------+----------+ | id | item_type_id | name | price | in_stock | is_taxed | +----+--------------+----------+--------+----------+----------+ | 1 | 2 | Purse | $200 | TRUE | TRUE | | 2 | 2 | Notebook | $14.99 | TRUE | TRUE | | 3 | 2 | Computer | $1,099 | FALSE | TRUE | +----+--------------+----------+--------+----------+----------+ Other Table: Item_Types +----+-----------+ | id | type_name | +----+-----------+ | 1 | recipes | | 2 | products | +----+-----------+
4 Réponses :
vous ne pouvez ajouter qu'une seule contrainte pour une colonne ou une paire de colonnes. Pensez aux pommes et aux oranges. Une colonne ne peut pas faire référence à la fois aux oranges et aux pommes. Il doit être orange ou pomme.
En remarque, ceci peut être réalisé d'une manière ou d'une autre avec des colonnes PERSISTED COMPUTED
, mais cela n'introduit que des frais généraux et de la complexité.
Merci, mais ce n'est pas utile
Même si vous ne trouvez pas cela utile, il répond complètement à votre question.
Il a depuis ajouté la partie de note d'accompagnement qui est très utile. Merci @Simonare
Je pense qu'il y a une faille dans la conception de votre base de données. La meilleure façon de résoudre votre problème réel est de regrouper les recettes et les produits dans un seul tableau. À l'heure actuelle, vous avez une colonne redondante dans chaque table appelée item_type_id. Cette colonne ne vaut rien, sauf si vous avez réellement les éléments dans la même table. Je dis redondant, car il a la même valeur pour absolument toutes les entrées de chaque table.
Vous avez deux options. Si vous ne pouvez pas modifier la conception de la base de données, travaillez sans clés étrangères et sélectionnez la couche logique parmi les tables correctes.
Ou, si vous pouvez modifier la conception de la base de données, faites en sorte que les produits et les recettes existent dans la même table. Vous avez déjà une table item_type, qui peut identifier la catégorisation des articles, il est donc logique de mettre tous les articles dans la même table
J'ai la possibilité de changer le schéma. Intéressant ... compte tenu des meilleures pratiques, ne serait-il pas "compliqué" de mettre deux types d'éléments dans le même tableau car toutes les colonnes ne seraient pas applicables à chaque entrée? Ou est-ce que je fabrique une montagne à partir d'une taupe?
Tout dépend de la portée et de la taille de votre application, je suppose. Pourquoi avez-vous besoin des données? Honnêtement, la conception des données d'inventaire est assez compliquée. J'ai peut-être répondu un peu prématurément. Mais il doit y avoir un moyen à la fois de pouvoir avoir des articles d'inventaire dans la même table, où les mêmes données s'appliquent, et de conserver également les données supplémentaires nécessaires pour chaque article séparées / normalisées
Dans ce cas, j'aurais peut-être juste travaillé sans les clés étrangères et utilisé une autre couche pour accéder aux tables pertinentes. ou découvrez ce que les deux tables ont en commun, extrayez-la dans une seule table, puis faites en sorte que deux tables distinctes de «données supplémentaires» aient des clés étrangères pour cette table. Parce que deux clés étrangères peuvent pointer facilement sur une colonne, alors que c'est moins courant dans le sens inverse
Vous pouvez ajouter des colonnes calculées à la table Inventory
:
ALTER TABLE Inventory ADD _recipe_item_id AS CASE WHEN item_type_id = 1 THEN item_id END persisted ALTER TABLE Inventory ADD _product_item_id AS CASE WHEN item_type_id = 2 THEN item_id END persisted
Vous pouvez ensuite ajouter deux clés étrangères distinctes aux deux tables, en utilisant ces deux colonnes à la place sur item_id
. Je suppose que la colonne item_type_id
dans ces deux tables est déjà calculée / contrainte de manière appropriée, mais sinon, vous voudrez peut-être en tenir compte également.
Parce que ces colonnes calculées sont NULL
lorsque le mauvais type est sélectionné, et parce que SQL Server ne vérifie pas les contraintes FK si au moins une valeur de colonne est NULL
, elles peuvent exister et seulement l'une ou l'autre le sera satisfait à tout moment.
Idée intéressante .. même si j'ai des tables supplémentaires telles que "Commandes" ou "Factures" qui nécessiteraient également ces deux colonnes. La meilleure approche est peut-être de retravailler la base de données afin que les produits et les recettes partagent la même table
Étant donné que vos produits et vos recettes sont stockés séparément et semblent avoir pour la plupart des colonnes séparées, des tableaux d'inventaire séparés sont probablement la bonne approche. par exemple
CREATE TABLE dbo.Products ( item_id BIGINT NOT NULL, Item_type_id AS 2, name VARCHAR(50) NOT NULL, Price DECIMAL(10, 4) NOT NULL, InStock BIT NOT NULL, CONSTRAINT PK_Products__ItemID PRIMARY KEY (item_id), CONSTRAINT FK_Products__Item_Type_ID FOREIGN KEY (Item_Type_ID) REFERENCES Item_Type (Item_Type_ID), CONSTRAINT FK_Products__ItemID_ItemTypeID FOREIGN KEY (item_id, Item_Type_ID) REFERENCES dbo.Item (item_id, item_type_id) );
Si vous avez besoin de tous les types combinés, vous pouvez simplement utiliser une vue:
CREATE TABLE dbo.Items ( item_id INT IDENTITY(1, 1) NOT NULL Item_type_id INT NOT NULL, CONSTRAINT PK_Items__ItemID PRIMARY KEY (item_id), CONSTRAINT FK_Items__Item_Type_ID FOREIGN KEY (Item_Type_ID) REFERENCES Item_Type (Item_Type_ID), CONSTRAINT UQ_Items__ItemID_ItemTypeID UNIQUE (Item_ID, Item_type_id) );
SI vous créez un nouveau type d'élément, alors vous besoin de modifier la conception de la base de données de toute façon pour créer une nouvelle table, il vous suffirait donc de modifier la vue en même temps
Une autre possibilité, serait d'avoir une seule table Items, puis d'avoir Products / Les recettes font référence à cela. Vous commencez donc par votre table items, dont chacun a un identifiant unique:
CREATE VIEW dbo.Inventory AS SELECT Product_id AS item_id, 2 AS item_type_id, [Count] FROM ProductInventory UNION ALL SELECT recipe_id AS item_id, 1 AS item_type_id [Count] FROM RecipeInventory; GO
Notez la clé unique ajoutée sur (item_id, item_type_id)
, ceci est important pour l'intégrité référentielle plus tard.
Ensuite, chacune de vos sous-tables a une relation 1: 1 avec ceci, donc votre table de produits deviendrait:
CREATE TABLE dbo.ProductInventory ( Product_id INT NOT NULL, [count] INT NOT NULL, CONSTRAINT FK_ProductInventory__Product_id FOREIGN KEY (Product_id) REFERENCES dbo.Product (Product_id) ); CREATE TABLE dbo.RecipeInventory ( Recipe_id INT NOT NULL, [count] INT NOT NULL, CONSTRAINT FK_RecipeInventory__Recipe_id FOREIGN KEY (Recipe_id) REFERENCES dbo.Recipe (Recipe_id ) );
Quelques points à noter:
item_id
est à nouveau la clé primaire, garantissant la relation 1: 1. (item_id, item_type_id)
retour à la table items. Cela garantit que vous ne pouvez insérer un enregistrement dans la table des produits que si l'enregistrement d'origine dans la table des articles a un item_type_id de 2. Une troisième option serait une table unique pour les recettes et les produits et rendrait toutes les colonnes non requises pour les deux nullables. Cette réponse sur les types d'héritage vaut la peine d'être lue.
C'est fantastique! Pour toute autre personne ayant un problème similaire au mien, je recommanderais également vivement de lire Héritage de table de classe (aka Table Per Type Inheritance) un modèle>. La solution que je propose est de créer un tableau unique d'articles avec des produits et des recettes qui le référencent. Merci à tous pour votre aide!
Veuillez rechercher l'héritage de modèle dans les bases de données relationnelles et les super / sous-tables.
Vous pouvez vraiment réfléchir à deux fois à sa conception. Il semble que vous fassiez une dénormalisation inutile et seule la réponse @KjetilNordin va droit au but. À titre de suggestion, vous pouvez créer une table d'éléments avec des colonnes de type et de nom et des recettes et produits de menaces comme détails pointant vers un élément