编辑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行的笛卡尔积,由于大量内存消耗,性能将崩溃。
我也非常想避免存储过程。
欢迎任何想法。
答案 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
(不确定字段名称是否正确,我无法理解架构描述!)