如何针对M:N关系优化数据库模型

时间:2013-04-08 16:33:30

标签: sql database-design relational-database

编辑2013年4月10日

为了使自己清楚,我正在添加另一个(简化的)示例,显示我想要实现的原则:

T1 - PERSONHAS                T2 - PRODUCTNEED
ANTON    has    WHEEL         CAR      need    ENGINE
ANTON    has    ENGINE        CAR      need    WHEEL
ANTON    has    NEEDLE        SHIRT    need    NEEDLE
BERTA    has    NEEDLE        SHIRT    need    THREAD
BERTA    has    THREAD        JAM      need    FRUIT
BERTA    has    ENGINE        JAM      need    SUGAR

Q3 - PERSONCANMAKE
ANTON    canmake    CAR
BERTA    canmake    SHIRT

Q4 - PERSONCANNOTMAKE
ANTON    cannotmake    SHIRT
ANTON    cannotmake    FRUIT
BERTA    cannotmake    CAR
BERTA    cannotmake    FRUIT

我有T1和T2,想要为Q3和Q4创建查询

结束编辑2013年4月10日

前言

为了创造产品(P),我需要具备某些通用功能(C - 如工厂,供应,电力,水等) 产品经理定义了创建他/她的产品所需的所有通用功能。

在某个位置(L)我有一些通用功能(C) 位置管理员定义他/她的位置能够提供的功能。这可能是明确的,明确的否,或者位置管理员根本没有列出某种能力。

数据库模型:

我创建了以下根实体

Location (PK: L) - values L1, L2, L3     // in real ca. 250 rows of L
Product (PK: P) - values P1, P2          // in real ca. 150 rows of P
Capability (PK: C) - values C1, C2, C3   // in real ca.  80 rows of C

以及以下子(依赖)实体

ProductCapabilityAssignment:P, C (PK: P, C, FK: P, C)
    P1 C1
    P1 C2
    P2 C1
    P2 C3

LocationCapabilityAssignment: L, C, Status (Y/N) (PK: L, C, FK: L, C)
    L1 C1 Y
    L2 C1 Y
    L2 C2 Y
    L2 C3 N
    L3 C1 Y
    L3 C2 Y
    L3 C3 Y

任务:

任务是确定某个产品是否可以在某个位置生产,从而为该产品定义的所有功能必须存在于该位置。为了回答这个问题,我无法帮助自己,但

创建位置和ProductCapabilityAssignment(CL_Cart)的笛卡尔积,以确保我为每个位置列出所有可能满足其需求的产品

CREATE VIEW CL_Cart AS
SELECT L.L, PCA.P, PCA.C
FROM Location AS L, ProductCapabilityAssignment AS PCA;

在CL_Cart和LocationCapabilityAssignment之间创建一个外部联接,以匹配一个位置可以提供的所有功能

CREATE VIEW Can_Produce AS
SELECT X.L, X.P, X.C, LCA.Status
FROM CL_CArt AS X LEFT JOIN LocationCapabilityAssignment AS LCA ON (X.C = LCA.C) AND (X.L = LCA.L);

所以我最终得到了

SELECT L, P, C, Status
FROM Can_Produce;
    L1 P1 C1 Y
    L1 P1 C2 NULL        // C2 not listed for L1
    L1 P2 C1 Y
    L1 P2 C3 NULL        // C3 not listed for L1
    L2 P1 C1 Y
    L2 P1 C2 Y
    L2 P2 C1 Y
    L2 P2 C3 N           // C3 listed as "No" for L2
    L3 P1 C1 Y
    L3 P1 C2 Y
    L3 P2 C1 Y
    L3 P2 C3 Y

意味着L1不能既不产生P1也不产生P2,L2不能产生P1,而L3可产生P1,P2。

因此,我可以向Can_Produce查询特定产品/位置,看看我拥有的功能和功能方面的功能。我也可以通过检查Status="N" OR Status is NULL提供一个整体YES / NO答案的快捷方式 - 如果是这样,产品就无法生产。

问题:

对于像MSSQL,MySQL,Oracle这样的关系型数据库(尚未决定且超出我的影响力),我想知道我是否为这种M:N关系选择了正确的数据模型,或者我是否可以做得更好。特别是我担心与ca. 250个位置,150个产品和一个产品平均由+/- 10个功能定义,所以说一个375.000行的笛卡尔积,由于大量内存消耗,性能将崩溃。

我也非常想避免存储过程。

欢迎任何想法。

2 个答案:

答案 0 :(得分:2)

     --Environment Variables    
Declare @Parent table (id int identity(1,1) primary key, Name varchar(20))  
Declare @Components table (id int identity(1,1) primary key, Name varchar(20))  Insert into @Components (Name) values ('Engine'),('Wheel'),('Chassis'),('NEEDLE'),('THREAD'),('FRUIT'),('SUGAR')
Declare @Objects table (id int identity(1,1) primary key, Name varchar(20)) 
Declare @Template table (id int identity(1,1) primary key, Name varchar(20), ObjectID int, ComponentID int) 
Insert into @Template (Name, ObjectID, ComponentID) 
Select 'Vehicle', O.ID, C.ID from @Objects O, @Components C where O.Name = 'Car' and C.Name in ('Engine','Wheel','Chassis')union    
Select 'Clothing', O.ID, C.ID from @Objects O, @Components C where O.Name = 'Shirt' and C.Name in ('Needle','Thread') union 
Select 'Confectionary', O.ID, C.ID from @Objects O, @Components C where O.Name = 'JAM' and C.Name in ('FRUIT','SUGAR')  
Declare @AvailableMaterials table (id int identity(1,1) primary key, TestType varchar(20), ParentID int, ComponentID int)   

--Test Data 
Insert into @AvailableMaterials (TestType,ParentID,ComponentID) 
Select 'CompleteSet', P.ID, T.ComponentID from @Parent P, @Template T where P.Name = 'Driver' and T.Objectid = (Select ID from @Objects where Name = 'Car') union   
Select 'CompleteSet', P.ID,  T.ComponentID from @Parent P, @Template T where P.Name = 'Seamstress' and T.Objectid = (Select ID from @Objects where Name = 'Shirt') union    
Select 'IncompleteSet', P.ID,  T.ComponentID from @Parent P, @Template T where P.Name = 'Confectionarist' and T.Objectid = (Select ID from @Objects where Name = 'Jam')     
and T.ComponentID not in (Select ID from @Components where Name = 'FRUIT')  




--/*What sets are incomplete?*/ 
Select *    
from @AvailableMaterials    
where ID in (   
Select  SRCData.ID  
from @AvailableMaterials SRCData cross apply (Select ObjectID from @Template T where ComponentID = SRCData.ComponentID) ObjectsMatchingComponents   
inner join @Template T  
on SRCData.ComponentID = T.ComponentID  
and T.ObjectID = ObjectsMatchingComponents.ObjectID 
cross apply (Select ObjectID, ComponentID from @Template FullTemplate where FullTemplate.ObjectID = T.ObjectID and FullTemplate.ComponentID not in (Select ComponentID from @AvailableMaterials SRC where SRC.ComponentID = FullTemplate.ComponentID)) FullTemplate 
)   


/*What sets are complete?*/ 
Select *    
from @AvailableMaterials    
where ID not in (   
Select  SRCData.ID  
from @AvailableMaterials SRCData cross apply (Select ObjectID from @Template T where ComponentID = SRCData.ComponentID) ObjectsMatchingComponents   
inner join @Template T  
on SRCData.ComponentID = T.ComponentID  
and T.ObjectID = ObjectsMatchingComponents.ObjectID 
cross apply (Select ObjectID, ComponentID from @Template FullTemplate where FullTemplate.ObjectID = T.ObjectID and FullTemplate.ComponentID not in (Select ComponentID from @AvailableMaterials SRC where SRC.ComponentID = FullTemplate.ComponentID)) FullTemplate 
)   

您好

这是我能想到的最好的......它的前提是你必须知道整套是什么,知道缺少什么。一旦你遗漏了什么,你可以从不完整的集合中告诉完整的集合。

我怀疑这个解决方案是否会很好地扩展,即使转移到带有索引的#tables也是如此。可能虽然......

我也有兴趣看到更清洁的方法。上述解决方案是在SQL 2012版本中开发的。注意交叉应用会稍微限制笛卡尔效应。

希望这有帮助。

答案 1 :(得分:1)

我不确定您使用的是哪个数据库,但这里有一个可以在sql server中使用的示例 - 不需要在其他数据库中进行许多更改......

WITH ProductLocation
AS
(
    SELECT  P.P,
            P.Name as ProductName,
            L.L,
            L.Name as LocationName
    FROM    Product P
    CROSS
    JOIN    Location L
),
ProductLocationCapability
AS
(
    SELECT  PL.P, 
            PL.ProductName, 
            PL.L, 
            PL.LocationName,
            SUM(PC.C) AS RequiredCapabilities,
            SUM(CASE WHEN LC.L IS NULL THEN 0 ELSE 1 END) AS FoundCapabilities
    FROM    ProductLocation PL
    JOIN    ProductCapabilityAssignment PC
            ON  PC.P = PL.P
    LEFT
    JOIN    LocationCapabilityAssignment LC
            ON  LC.L = PL.L
            AND LC.C = PC.C
    GROUP BY PL.P, PL.ProductName, PL.L, PL.LocationName
)
SELECT  PLC.P, 
        PLC.ProductName,
        PLC.L,
        PLC.LocationName,
        CASE WHEN PLC.RequiredCapabilities = PLC.FoundCapabilities THEN 'Y' ELSE 'N' END AS CanMake
FROM    ProductLocationCapability PLC

(不确定字段名称是否正确,我无法理解架构描述!)