2

编辑 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) 的笛卡尔积,以确保对于每个位置,我列出所有可能的产品及其 cpability 需求

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——如果是这样,则无法生产产品。

问题:

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

我也很想避免存储过程。

任何想法都会受到欢迎。

4

2 回答 2

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 版本中开发的。注意 cross apply 这在一定程度上限制了笛卡尔效应。

希望这可以帮助。

于 2013-11-26T13:29:33.490 回答
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

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

于 2013-11-26T13:56:41.953 回答