SQL Server:两个条件逻辑上的递归高级查询未显示层次结构关系

时间:2015-06-17 03:53:54

标签: sql sql-server tsql sql-server-2012 common-table-expression

最近我遇到了在SQL Server 2012中解决的挑战。这是问题的背景。

我们在产品实体中维护自引用实体(层次结构)。每种产品都有父母和子女的关系。

产品正在进行特殊的分组,称为主产品,它基于以下逻辑推导出来。

如果层次结构中只有一个产品可用而不考虑IsMasterProdcut标记,则认为是主帐户。

对于其直接母产品标记为主产品或最重要产品的其他产品,首先将其视为主产品。

图形表示如下,

http://i.stack.imgur.com/ij6Bp.jpg

这是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

基本上我写的这个查询并没有深入到更深层次。它从父级终止。我得到的第二个观察是,这确实取得了不是主产品的父节点。

我的方法有误吗?除了游标之外,我能做到最好的方法是什么。

1 个答案:

答案 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订购商品,以便将其打印出来。

SqlFiddle here