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 之前订购商品,以便将它们打印出来。

SqlFiddle here