SQL从Parent Rows获取第一个not null值

时间:2009-07-05 04:38:49

标签: sql sql-server

我现在几次遇到这个问题,我想找到一个更好的方法来解决这个问题。

基本上我有一个具有子类别的类别。如果子类别没有描述我想检索父类别描述。

当我有Sub Sub类别时,它开始变得困难。

Sql表如下所示:

CategoryID         int,
ParentCategoryID   int null,
Name               varchar(255),
Description        varchar(MAX) null

我想创建一个查找Description的函数,但我不知道这是否是最佳解决方案或如何创建函数。

我主要是寻找解决此问题的最佳/正确方法。

4 个答案:

答案 0 :(得分:3)

假设:

  1. CategoryId是主键。

  2. 如果行级别的描述为空,请查看父行。如果Parent行具有null描述,请查看parent的父级。等等。换句话说,使用祖先的第一个非空描述。

  3. 如果行级别描述为空,并且没有祖先存在非空描述,则整体描述为空

  4. 设置示例表和测试数据。

    create table #SO (CategoryID int primary key
        , ParentCategoryID int Null
        , Name varchar(255) not null
        , Description varchar(MAX) Null
        )
    
    insert into #SO (CategoryID, ParentCategoryID, Name, Description)
    values (1, null, 'Top 1', 'Top 1 Description')
        , (2, null, 'Top 2', 'Top 2 Description')
        , (3, null, 'Top 3', null)
        , (11, 1, 'Child 11', 'Child 11 Description')
        , (12, 1, 'Child 12', null)
        , (21, 2, 'Child 21', null)
        , (211, 21, 'Child 211', null)
        , (2111, 211, 'Child 2111', null)
        , (2112, 211, 'Child 2112', 'Child 2112 Description')
        , (31, 3, 'Child 31', 'Child 31 Description')
        , (32, 3, 'Child 32', null)
    

    使用递归CTE。请注意,树向上走。我们从所有行开始,然后根据需要查看父项,而不是从树的顶部开始正常的树操作并继续工作。

      ; with Description (BaseCategoryId
            , CurrentParentCategoryId
        , CurrentDescription
        , CurrentLevel)
       as
    (-- Anchor -- Start with all rows in the table.
     select CategoryId as BaseCategoryId
        , ParentCategoryId as CurrentParentCategoryId
        , Description as CurrentDescription
        , 0 as CurrentLevel
    from #SO    -- Recursive -- We are walking up the tree from all nodes, 
        -- We only continue up the tree when we do not have a description yet.
    union all
    select D.BaseCategoryId
        , so.ParentCategoryId
        , so.Description
        , D.CurrentLevel + 1
    from #SO so
    inner join Description D
        on D.CurrentParentCategoryId = so.CategoryId
        and D.CurrentDescription is null)
     select DL.BaseCategoryId as CategoryId
       , DL.CurrentDescription as UltimateDescription
     -- Now self outer join with the CTE every step of the walk
     -- for each BaseCategoryId, and then filter all but the top 
     -- level. (Level is measured as distance from base.)
     from Description as DL
     left outer join Description as DR
    on DL.BaseCategoryId = DR.BaseCategoryId
    and DL.CurrentLevel < DR.CurrentLevel
     where DR.BaseCategoryId is null 
     order by DL.BaseCategoryId
    

    输出是从CategoryId到最终描述的映射。

    在重复使用方面,我会将上述内容视为一种观点。

答案 1 :(得分:1)

鉴于您的结构,Alex Martelli的解决方案可能是您找到的最佳解决方案。如果您可以更改模型,另一个选项是从链接列表树结构更改为嵌套集模型。

Joe Celko有a book on trees and hierarchies,它可以通过各种方式对其进行建模以及每种方法的优缺点。您也可以通过Google或Google网上论坛找到有关该主题的大量信息。

嵌套集模型的一个缺点是对树结构的更改有点贵,但对于产品,您通常检索的内容远远超过更新。特别是在类别之间移动,这在大多数商业案例中通常很少见。

使用嵌套集模型,以下内容可以为您提供所需内容:

SELECT
     P1.Name,
     COALESCE(P1.Description, P2.Description) AS Description
FROM
     Products P1
LEFT OUTER JOIN Products P2 ON
     P2.lft < P1.lft AND
     P2.rgt > P1.rgt AND
     P2.Description IS NOT NULL
LEFT OUTER JOIN Products P3 ON
     P3.lft < P1.lft AND P3.lft > P2.lft AND
     P3.rgt > P1.rgt AND P3.rgt < P2.rgt AND
     P3.Description IS NOT NULL
WHERE
     P3.ID IS NULL

答案 2 :(得分:1)

如果您需要递归地遍历父树,请参阅answer from Alex。如果您只需要一个级别,那么简单的LEFT JOIN应该有效:

SELECT      c.CategoryID,
            c.ParentCategoryID,
            c.Name,
            COALESCE(c.Description, p.Description) AS Description
FROM        dbo.Category c
LEFT JOIN   dbo.Category p
        ON  c.ParentCategoryID = p.CategoryID

答案 3 :(得分:0)

显然,您需要两个表引用和一个连接。这将使您可以访问这两个描述。然后,您需要一个inline if语句来选择要使用的值,基于第一个为null。

这样的东西应该有用(表名为Table1)

SELECT Table1.CategoryID, IIf(Parent.Description Is Null, Table1.description, parent.description) AS [Desc]
FROM Table1 INNER JOIN Table1 AS Parent ON Table1.CategoryID = Parent.ParentCategoryID;