SQL 服务器:对两个条件逻辑的递归高级查询未显示层次结构关系
SQL Server : Recursive Advanced Query on two conditional logics Not showing hierarchy relationship
最近我在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
基本上我写的这个查询并没有深入到更深层次。它从父级终止。我的第二个观察是,这确实拾取了不是主产品的父节点。
是我的做法错了吗?除了游标之外,我完成这项工作的最佳方式是什么。
您走在正确的轨道上 :) 一些修正:
您在产品 8 和 10 中插入零,而不是空值 ParentId
- 这就是为什么它们从未在您的初始 #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 层的层次结构,即
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
之前订购商品,以便将它们打印出来。
最近我在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
基本上我写的这个查询并没有深入到更深层次。它从父级终止。我的第二个观察是,这确实拾取了不是主产品的父节点。
是我的做法错了吗?除了游标之外,我完成这项工作的最佳方式是什么。
您走在正确的轨道上 :) 一些修正:
您在产品 8 和 10 中插入零,而不是空值 ParentId
- 这就是为什么它们从未在您的初始 #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 层的层次结构,即
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
之前订购商品,以便将它们打印出来。