最近我遇到了在SQL Server 2012中解决的挑战。这是问题的背景。
我们在产品实体中维护自引用实体(层次结构)。每种产品都有父母和子女的关系。
产品正在进行特殊的分组,称为主产品,它基于以下逻辑推导出来。
如果层次结构中只有一个产品可用而不考虑IsMasterProdcut
标记,则认为是主帐户。
对于其直接母产品标记为主产品或最重要产品的其他产品,首先将其视为主产品。
图形表示如下,
这是DDL
-- Create Table
CREATE TABLE Product
(
ProductID int PRIMARY KEY,
Name VARCHAR(30) NOT NULL,
ParentId int,
IsMasterProdcut bit NOT NULL
)
-- Insert the data to the table
-- Senario where top most product is the master product
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (1,'Prodcut 1',NULL,0); -- <-- this is the master prodcut as non of the child as flaged
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (2,'Prodcut 2',1,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (3,'Prodcut 3',2,0);
-- Senario two where in middnle account has flag as master product
INSERT INTO Product (ProductID,name,ParentId,IsMasterProdcut) VALUES (4,'Prodcut 4',NULL,0); -- <-- this is the master prodcut as this is top most in hirerachy . So 4 will be master prodcut of 4 and 5 , 6 and 7 will not master product
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (5,'Prodcut 5',4,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (6,'Prodcut 6',5,1); -- < -- this a a master prodcut as it is flagged as master product , So account 7 and 6 master product with be 6
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (7,'Prodcut 7',6,0);
-- Senario three where it has one product
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (8,'Prodcut 8',0,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (9,'Prodcut 9',0,1);
-- Senario 4 Complex product
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (10,'Prodcut 10',0,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (11,'Prodcut 11',10,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (12,'Prodcut 12',11,1);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (13,'Prodcut 13',12,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (14,'Prodcut 14',10,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (15,'Prodcut 15',14,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (16,'Prodcut 16',15,0);
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut) VALUES (17,'Prodcut 17',10,0);
预期结果
Master Product ID Master Product Name Product ID Product Name
1 Product 1 1 Product 1
1 Product 1 2 Product 2
1 Product 1 3 Product 3
4 Product 4 4 Product 4
4 Product 4 5 Product 5
6 Product 6 6 Product 6
6 Product 6 7 Product 7
8 Product 8 8 Product 8
9 Product 9 9 Product 9
10 Product 10 10 Product 10
10 Product 10 11 Product 11
12 Product 12 12 Product 12
12 Product 12 13 Product 13
10 Product 10 14 Product 14
10 Product 10 15 Product 15
10 Product 10 16 Product 16
10 Product 10 17 Product 17
工作解决方案:这是我到目前为止的解决方案:
BEGIN
CREATE TABLE #TmpMasterProduct
(
ProductId nvarchar(50)
)
INSERT INTO #TmpMasterProduct
SELECT ProductId
FROM (
-- Get master accounts which are flagged as master product
SELECT MA.ProductId
FROm [dbo].[Product] AS MA WITH (NOLOCK)
WHERE MA.[IsMasterProdcut] = 1
UNION
-- Get top most prodcut which will be automatically consider as master product.
SELECT MAT.ProductId
FROM DBO.[Product] As MAT WITH (NOLOCK)
WHERE MAT.[ParentId] IS NULL
) AS MasterProdcuts;
WITH Mapping as
(
SELECT A.ProductId , A.ParentId
FROM DBO.[Product] A
WHERE A.ProductId IN
(
SELECT ProductId
FROM #TmpMasterProduct
)
UNION ALL
SELECT A.ProductId , A.ParentId
FROM DBO.[Product] A
INNER JOIN Mapping M
ON M.ProductId = A.ParentID
)
SELECT M.ParentId As MasterProductId , MP.Name As MasterProductName , M.ProductId As ProdcutId , CP.Name As ProductName
From Mapping As M
LEFT OUTER JOIN DBO.Product As MP ON MP.ProductId = M.ParentId
LEFT OUTER JOIN DBO.Product As CP On CP.ProductId = M.ProductId
DROP TABLE #TmpMasterProduct
END
但我偏离了我想要的结果。这是我目前的最新消息。
MasterProductId MasterProductName ProdcutId ProductName
NULL NULL 1 Prodcut 1
NULL NULL 4 Prodcut 4
5 Prodcut 5 6 Prodcut 6
0 NULL 9 Prodcut 9
11 Prodcut 11 12 Prodcut 12
12 Prodcut 12 13 Prodcut 13
6 Prodcut 6 7 Prodcut 7
4 Prodcut 4 5 Prodcut 5
5 Prodcut 5 6 Prodcut 6
6 Prodcut 6 7 Prodcut 7
1 Prodcut 1 2 Prodcut 2
2 Prodcut 2 3 Prodcut 3
基本上我写的这个查询并没有深入到更深层次。它从父级终止。我得到的第二个观察是,这确实取得了不是主产品的父节点。
我的方法有误吗?除了游标之外,我能做到最好的方法是什么。
答案 0 :(得分:1)
你走在正确的轨道上:)一些修正:
您将ParentId
作为零(而不是空)插入到产品8和10中 - 这就是为什么它们永远不会在您的初始#TmpMasterProduct
查询中被选中 - 您需要更改这些回到NULL例如
INSERT INTO Product (ProductID,Name,ParentId,IsMasterProdcut)
VALUES (8,'Prodcut 8',NULL,0);
(另外,如果您在自联接时使用ParentId -> ProductId
的外键强制引用完整性,则会阻止此类问题)
您不需要主产品预过滤器上的UNION
- 您只需使用OR
,即:
INSERT INTO #TmpMasterProduct
SELECT MA.ProductId
FROM
[dbo].[Product] AS MA
WHERE MA.[IsMasterProdcut] = 1 OR MA.[ParentId] IS NULL;
在您的递归CTE中,您需要记住每个主产品树的实际MasterProductId
,而不一定是ParentId
,以允许大于1的层次结构,即< / p>
WITH Mapping as
(
SELECT A.ProductId as MasterProductId, A.ProductId , A.ParentId ...
UNION ALL
SELECT M.MasterProductId, A.ProductId , A.ParentId ...
您需要在导航树时引入终止条件,当有一个节点本身就是主产品时会终止(这将单独列出)。
AND A.IsMasterProdcut = 0
您希望按MasterProductId
订购商品,以便将其打印出来。