Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/oop/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 如何向引用表B(id,类型)或表C(id,类型)的表A(id,类型)添加外键约束?_Sql_Sql Server_Azure Sql Database - Fatal编程技术网

Sql 如何向引用表B(id,类型)或表C(id,类型)的表A(id,类型)添加外键约束?

Sql 如何向引用表B(id,类型)或表C(id,类型)的表A(id,类型)添加外键约束?,sql,sql-server,azure-sql-database,Sql,Sql Server,Azure Sql Database,我希望使用表A中的两列作为两个表(表B或表C)之一的外键。使用列Table_A.item_id和Table_A.item_type_id,我希望强制任何新行在表B或表C中具有匹配的item_id和item_type_id 示例: Table A: Inventory +---------+--------------+-------+ | item_id | item_type_id | count | +---------+--------------+-------+ | 2

我希望使用表A中的两列作为两个表(表B或表C)之一的外键。使用列Table_A.item_id和Table_A.item_type_id,我希望强制任何新行在表B或表C中具有匹配的item_id和item_type_id

示例:

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  |
+----+-----------+
我希望能够有一个库存表,员工可以在其中输入库存计数,无论项目是配方还是产品。我不想有一个product_inventory和recipe_inventory表,因为无论项目类型如何,我都需要对所有库存项目执行许多操作

一种解决方案是创建一个参考表,如下所示:

Table CD: Items
+---------+--------------+------------+-----------+
| item_id | item_type_id | product_id | recipe_id |
+---------+--------------+------------+-----------+
|       2 |            1 | NULL       | 2         |
|       3 |            1 | NULL       | 3         |
|       1 |            2 | 1          | NULL      |
+---------+--------------+------------+-----------+
这看起来很麻烦,加上我现在需要在这个新表中添加/删除产品/配方,无论它们何时从各自的表中添加/删除。(是否有一种自动方式来实现这一点?)

我真正想做的是这样的事情

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])}
也许没有我描述的解决方案,所以如果你有任何想法,我可以保持相同/相似的模式,我绝对愿意听他们!
谢谢:)

您只能为一列或一对列添加一个约束。想想苹果和桔子。列不能同时引用桔子和苹果。它必须是橙色或苹果色

作为旁注,这可以通过
持久化计算列
以某种方式实现,但它只会带来开销和复杂性


我认为您的数据库设计存在缺陷。解决实际问题的最佳方法是将Recipes和products作为一张表。现在,每个表中都有一个称为item\u type\u id的冗余列。该列没有任何价值,除非您在同一个表中实际有这些项。我之所以说冗余,是因为它对每个表中的每个条目都有相同的值

你有两个选择。如果无法更改数据库设计,请在没有外键的情况下工作,并使逻辑层从正确的表中进行选择


或者,如果可以更改数据库设计,则使产品和Recipe存在于同一个表中。您已经有一个item_type表,它可以识别项目分类,因此将所有项目放在同一个表中是有意义的

您可以将一些计算列添加到
库存
表中:

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
然后,您可以使用这两列而不是
item\u id
向这两个表添加两个单独的外键。我假设这两个表中的<代码> ITEMyType ID ID/COD>列已经适当地计算/约束,但如果不是,您可能也会考虑这一点。


由于选择错误类型时,这些计算列为
NULL
,并且如果至少有一个列值为
NULL
,SQL Server不会检查FK约束,因此它们都可以存在,并且在任何时候都只能满足其中一个。

因为产品和配方是分开存储的,而且似乎大部分都有单独的列,那么单独的库存表可能是正确的方法。e、 g

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 )
);
如果需要组合所有类型,只需使用视图即可:

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
如果您创建了一个新的item_类型,那么您仍然需要修改DB设计以创建一个新表,因此您只需要同时修改视图

另一种可能性是,创建一个单一项目表,然后让产品/配方引用该表。因此,您从items表开始,每个表都有一个唯一的ID:

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)
);
请注意在
(item\u id,item\u type\u id)
上添加的唯一键,这对于以后的引用完整性非常重要

然后,每个子表都与此有1:1的关系,因此产品表将变成:

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)
);
需要注意的几点:

  • item_id
    再次成为主键,确保了1:1的关系
  • 计算列item_type_id(as 2)确保所有item_type_id设置为2。这是一个关键点,因为它允许添加外键约束
  • (item\u id,item\u type\u id)
    上的外键返回items表。这样可以确保,如果items表中的原始记录的item_type_id为2,则只能将记录插入product表

第三个选项是配方和产品的单个表,并使这两个表中不需要的任何列都可以为空。非常值得一读。

谢谢,但这没有帮助即使你觉得这没有帮助,他也会完整地回答你的问题。他后来添加了非常有用的旁注部分。谢谢@SimonareI有能力更改模式。有趣的。。。考虑到最佳实践,将两种项目类型放在同一个表中不是很“混乱”,因为不是每一列都适用于每个条目吗?或者说我是在小题大做?我想这完全取决于你的应用程序的范围和大小。你需要这些数据做什么?老实说,库存数据设计相当复杂。我可能回答得有点过早。但必须有一种方法,既能在同一个表中有库存项目,也能将每个项目所需的额外数据分开/规范化。在这种情况下,我可能只是在没有外键的情况下工作,并使用另一层访问相关表。或者找出这两个表的共同点,将其提取到一个表中,然后使两个单独的“额外数据”表具有该表的外键。因为两个外键可能很容易指向一列,而相反的情况则不太常见。请搜索关系数据库和超级/子表中的模型继承。您可以仔细考虑他的设计。看起来你正在做一个不必要的反规范化,只有@KjetilNordin的答案才说到点子上。建议您创建一个项目ta
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)
);